API reference

Registration

patty.registration.get_stick_scale(pointcloud, eps=0.1, min_samples=20)[source]

Takes a point cloud, as a numpy array, looks for red segments of scale sticks and returns the scale estimation with most support. Method: pointcloud –dbscan–> clusters –lengthEstimation–>

lengths –ransac–> best length
Arguments:
pointcloud Point cloud containing only measuring stick segments
(only the red, or only the white parts)
eps DBSCAN parameter: Maximum distance between two samples
for them to be considered as in the same neighborhood.
min_samples DBSCAN parameter: The number of samples in a neighborhood
for a point to be considered as a core point.
Returns:
scale Estimate of the size of one actual meter in expressed
in units of the pointcloud’s coordinates.
confidence A number expressing the reliability of the estimated
scale. Confidence is in [0, 1]. With a confidence greater than .5, the estimate can be considered useable for further calculations.
patty.registration.align_footprints(loose_pc, fixed_pc, allow_scaling=True, allow_rotation=True, allow_translation=True)[source]

Align a pointcloud ‘loose_pc’ by placing it on top of ‘fixed_pc’ as good as poosible. Done by aligning the principle axis of both pointclouds.

NOTE: Both pointclouds are assumed to be the footprint (or projection) on the xy plane, with basically zero extent along the z-axis.

(allow_rotation=True)
The pointcloud boundary is alinged with the footprint by rotating its pricipal axis in the (x,y) plane.
(allow_translation=True)
Then, it is translated so the centers of mass coincide.
(allow_scaling=True)
Finally, the pointcloud is scaled to have the same extent.
Arguments:

loose_pc : pcl.PointCloud fixed_pc : pcl.PointCloud

allow_scaling : Bolean allow_rotation : Bolean allow_translation : Bolean

Returns:
rot_matrix, rot_center, scale, translation : np.array()
patty.registration.coarse_registration(pointcloud, drivemap, footprint, downsample=None)[source]

Improve the initial registration. Find the proper scale by looking for the red meter sticks, and calculate and align the pointcloud’s footprint.

Arguments:
pointcloud: pcl.PointCloud
The high-res object to register.
drivemap: pcl.PointCloud
A small part of the low-res drivemap on which to register
footprint: pcl.PointCloud
Pointlcloud containing the objects footprint
downsample: float, default=None, no resampling
Downsample the high-res pointcloud before footprint calculation.
patty.registration.estimate_pancake_up(pointcloud)[source]

Assuming a pancake like pointcloud, the up direction is the third PCA.

patty.registration.find_rotation_xy(pc, ref)[source]

Find the transformation that rotates the principal axis of the pointcloud onto those of the reference. Keep the z-axis pointing upwards.

Arguments:

pc: pcl.PointCloud

ref: pcl.PointCloud

Returns:
numpy array of shape [3,3], can be used to rotate pointclouds with pc.rotate()
patty.registration.fine_registration(pointcloud, drivemap, center, voxelsize=0.05)[source]

Final registration step using ICP.

Find the local optimal postion of the pointcloud on the drivemap; due to our coarse_registration algorithm, we have to try two orientations: original, and rotated by 180 degrees around the z-axis.

Arguments:
pointcloud: pcl.PointCloud
The high-res object to register.
drivemap: pcl.PointCloud
A small part of the low-res drivemap on which to register
center: np.array([3])
Vector giving the centerpoint of the pointcloud, used to do the 180 degree rotations.
voxelsize: float default : 0.05
Size in [m] of the voxel grid used for downsampling
patty.registration.initial_registration(pointcloud, up, drivemap, initial_scale=None, trust_up=True)[source]

Initial registration adds an spatial reference system to the pointcloud, and place the pointlcoud on top of the drivemap. The pointcloud is rotated so that the up vector points along [0,0,1], and scaled such that it has the right order of magnitude in size.

Arguments:
pointcloud : pcl.PointCloud
The high-res object to register.
up: np.array([3])
Up direction for the pointcloud. If None, assume the object is pancake shaped, and chose the upvector such that it is perpendicullar to the pancake.
drivemap : pcl.PointCloud
A small part of the low-res drivemap on which to register.
initial_scale : float
if given, scale pointcloud using this value; estimate scale factor from bounding boxes.
trust_up : Boolean, default to True

True: Assume the up vector is exact. False: Calculate ‘up’ as if it was None, but orient it such that

np.dot( up, pancake_up ) > 0

NOTE: Modifies the input pointcloud in-place, and leaves it in a undefined state.

patty.registration.rotate_upwards(pc, up)[source]

Rotate the pointcloud in-place around its center, such that the ‘up’ vector points along [0,0,1]

Arguments:
pc : pcl.PointCloud up : np.array([3])
Returns:
pc : pcl.PointCloud the input pointcloud, for convenience.

Segmentation

patty.segmentation.dbscan_labels(pointcloud, epsilon, minpoints, rgb_weight=0, algorithm='ball_tree')[source]

Find an array of point-labels of clusters found by the DBSCAN algorithm.

pointcloud : pcl.PointCloud
Input pointcloud.
epsilon : float
Neighborhood radius for DBSCAN.
minpoints : integer
Minimum neighborhood density for DBSCAN.
rgb_weight : float, optional
If non-zero, cluster on color information as well as location; specifies the relative weight of the RGB components to spatial coordinates in distance computations. (RGB values have wildly different scales than spatial coordinates.)
labels : Sequence
A sequence of labels per point. Label -1 indicates a point does not belong to any cluster, other labels indicate the cluster number a point belongs to.
patty.segmentation.get_largest_dbscan_clusters(pointcloud, min_return_fragment=0.7, epsilon=0.1, minpoints=250, rgb_weight=0)[source]

Finds the largest clusters containing together at least min_return_fragment of the complete point cloud. In case less points belong to clusters, all clustered points are returned.

pointcloud : pcl.PointCloud
Input pointcloud.
min_return_fragment : float
Minimum desired fragment of pointcloud to be returned
epsilon : float
Neighborhood radius for DBSCAN.
minpoints : integer
Minimum neighborhood density for DBSCAN.
rgb_weight : float, optional
If non-zero, cluster on color information as well as location; specifies the relative weight of the RGB components to spatial coordinates in distance computations. (RGB values have wildly different scales than spatial coordinates.)
cluster : pcl.PointCloud
Registered pointcloud of the largest cluster found by dbscan.
patty.segmentation.segment_dbscan(pointcloud, epsilon, minpoints, **kwargs)[source]

Run the DBSCAN clustering+outlier detection algorithm on pointcloud.

pointcloud : pcl.PointCloud
Input pointcloud.
epsilon : float
Neighborhood radius for DBSCAN.
minpoints : integer
Minimum neighborhood density for DBSCAN.
**kwargs : keyword arguments, optional
arguments passed to _dbscan_labels

clusters : iterable over registered PointCloud

patty.segmentation.get_red_mask(pointcloud)[source]

Returns a mask for the red parts of a pointcloud.

Red points are points that have hue larger than 0.9 and saturation larger than 0.5 in HSV colorspace.

patty.segmentation.boundary_of_center_object(pc, downsample=None, angle_threshold=0.1, search_radius=0.1, normal_search_radius=0.1)[source]

Find the boundary of the main object. First applies dbscan to find the main object, then estimates its footprint by taking the pointcloud boundary. Resulting pointcloud has the same SRS and offset as the input.

Arguments:

pointcloud : pcl.PointCloud

downsample : If given, reduce the pointcloud to given percentage
values should be in [0,1]

angle_threshold : float defaults to 0.1

search_radius : float defaults to 0.1

normal_search_radius : float defaults to 0.1

Returns:
boundary : pcl.PointCloud
patty.segmentation.boundary_of_drivemap(drivemap, footprint, height=1.0, edge_width=0.25)[source]

Construct an object boundary using the manually recorded corner points. Do this by finding all the points in the drivemap along the footprint. Use the bottom ‘height’ meters of the drivemap (not trees). Resulting pointcloud has the same SRS and offset as the input.

Arguments:

drivemap : pcl.PointCloud footprint : pcl.PointCloud height : Cut-off height, points more than this value above the

lowest point of the drivemap are considered trees, and dropped. default 1 m.
edge_width : Points belong to the boundary when they are within
this distance from the footprint. default 0.25
Returns:
boundary : pcl.PointCloud
patty.segmentation.boundary_of_lowest_points(pc, height_fraction=0.01)[source]

Construct an object boundary by taking the lowest (ie. min z coordinate) fraction of points. Resulting pointcloud has the same SRS and offset as the input.

Arguments:
pc : pcl.PointCloud height_fraction : float
Returns:
boundary : pcl.PointCloud

SRS

patty.srs.force_srs(pc, srs=None, offset=None, same_as=None)[source]

Set a spatial reference system (SRS) and offset for a pointcloud. Either give a SRS and offset, or a reference pointcloud This function affects the metadata only.

This is the recommended way to turn a python-pcl pointcloud to a registerd pointcloud with absolute coordiantes.

NOTE: To change the SRS for an already registered pointcloud, use set_srs()

Example:

# set the SRS to lat/lon, leave offset unchanged force_srs( pc, srs=”EPSG:4326” )
Arguments:

pc : pcl.Pointcloud

same_as : pcl.PointCloud

offset : np.array([3])
Must be added to the points to get absolute coordinates, neccesary to retain precision for LAS pointclouds.
srs : object or osgeo.osr.SpatialReference
If it is an SpatialReference, it will be used directly. Otherwise it is passed to osr.SpatialReference.SetFromUserInput()
Returns:
pc : pcl.PointCloud
The input pointcloud.
patty.srs.is_registered(pointcloud)[source]

Returns True when a pointcloud is registered; ie coordinates are relative to a specific spatial reference system or offset.

In that case, first transform one pointcloud to the reference system of the other, before doing processing on the points:

set_srs(pcA, same_as=pcB)
patty.srs.same_srs(pc_one, pc_two)[source]

True if the two pointclouds have the same coordinate system

Arguments:
pc_one : pcl.PointCloud pc_two : pc..PointCloud
patty.srs.set_srs(pc, srs=None, offset=array([ 0., 0., 0.]), same_as=None)[source]

Set the spatial reference system (SRS) and offset for a pointcloud. This function transforms all the points to the new reference system, and updates the metadata accordingly.

Either give a SRS and offset, or a reference pointcloud

NOTE: Pointclouds in PCL do not have absolute coordinates, ie.
latitude / longitude. This function sets metadata to the pointcloud describing an absolute frame of reference. It is left to the user to make sure pointclouds are in the same reference system, before passing them on to PCL functions. This can be checked with patty.utils.same_srs().
NOTE: To add a SRS to a point cloud, or to update incorrect metadata,
use force_srs().

Example:

# set the SRS to lat/lon, # don’t use an offset, so it defaults to [0,0,0] set_srs( pc, srs=”EPSG:4326” )
Arguments:

pc : pcl.Pointcloud, with pcl.is_registered() == True

same_as : pcl.PointCloud

offset : np.array([3], dtype=np.float64 )
Must be added to the points to get absolute coordinates, neccesary to retain precision for LAS pointclouds.
srs : object or osgeo.osr.SpatialReference
If it is an SpatialReference, it will be used directly. Otherwise it is passed to osr.SpatialReference.SetFromUserInput()
Returns:
pc : pcl.PointCloud
The input pointcloud.

Utils

Pointcloud functions for reading/writing LAS files, and functions for dealing with the spatial reference system.

class patty.utils.BoundingBox(points=None, min=None, max=None)[source]

A bounding box for a sequence of points.

Center, size and diagonal are updated when the minimum or maximum are updated.

Constructor usage: either set points (any object that is converted to an NxD array by np.asarray, with D the number of dimensions) or a fixed min and max.

center

Center point of the bounding box

contains(pos)[source]

Whether the bounding box contains given position.

diagonal

Length of the diagonal of the box.

size

N-dimensional size array

patty.utils.clone(pc)[source]

Return a copy of a pointcloud, including registration metadata

Arguments:
pc: pcl.PointCloud()
Returns:
cp: pcl.PointCloud()
patty.utils.downsample_random(pc, fraction, random_seed=None)[source]

Randomly downsample pointcloud to a fraction of its size.

Returns a pointcloud of size fraction * len(pc), rounded to the nearest integer. Resulting pointcloud has the same SRS and offset as the input.

Use random_seed=k for some integer k to get reproducible results. Arguments:

pc : pcl.PointCloud
Input pointcloud.
fraction : float
Fraction of points to include.
random_seed : int, optional
Seed to use in random number generator.
Returns:
pcl.Pointcloud
patty.utils.downsample_voxel(pc, voxel_size=0.01)[source]

Downsample a pointcloud using a voxel grid filter. Resulting pointcloud has the same SRS and offset as the input.

Arguments:
pc : pcl.PointCloud
Original pointcloud
float : voxel_size
Grid spacing for the voxel grid
Returns:
pc : pcl.PointCloud
filtered pointcloud
patty.utils.extract_mask(pointcloud, mask)[source]

Extract all points in a mask into a new pointcloud.

Arguments:
pointcloud : pcl.PointCloud
Input pointcloud.
mask : numpy.ndarray of bool
mask for which points from the pointcloud to include.
Returns:
pointcloud with the same registration (if any) as the original one.
patty.utils.load(path, format=None, load_rgb=True)[source]

Read a pointcloud file.

Supports LAS and CSV files, and lets PCD and PLY files be read by python-pcl.

Arguments:
path : string
Filename.
format : string, optional
File format: “PLY”, “PCD”, “LAS”, “CSV”, or None to detect the format from the file extension.
load_rgb : bool
Whether RGB is loaded for PLY and PCD files. For LAS files, RGB is always read.
Returns:
pc : pcl.PointCloud
patty.utils.log(*args, **kwargs)[source]

Simple logging function that prints to stdout

patty.utils.make_las_header(pointcloud)[source]

Make a LAS header for given pointcloud.

If the pointcloud is registered, this is taken into account for the header metadata.

LAS rounds the coordinates on writing; this is controlled via the ‘precision’ attribute of the input pointcloud. By default this is 0.01 in units of the projection.

Arguments:
pointcloud : pcl.PointCloud
Input pointcloud.
Returns:
header : liblas.header.Header
Header for writing the pointcloud to a LAS file.
patty.utils.measure_length(pointcloud)[source]

Returns the length of a point cloud in its longest direction.

patty.utils.save(cloud, path, format=None, binary=False, las_header=None)[source]

Save a pointcloud to file.

Supports LAS and CSV files, and lets PCD and PLY files be saved by python-pcl.

Arguments:
cloud : pcl.PointCloud or pcl.PointCloudXYZRGB
Pointcloud to save.
path : string
Filename.
format : string
File format: “PLY”, “PCD”, “LAS”, “CSV”, or None to detect the format from the file extension.
binary : boolean
Whether PLY and PCD files are saved in binary format.
las_header: liblas.header.Header
LAS header to use. When none, a default header is created by make_las_header(). Default: None