pointtree.instance_segmentation
Algorithms for tree instance segmentation.
- class pointtree.instance_segmentation.CoarseToFineAlgorithm(
- trunk_class_id: int,
- crown_class_id: int,
- *,
- branch_class_id: int | None = None,
- algorithm: Literal['full', 'watershed_crown_top_positions', 'watershed_matched_tree_positions'] = 'full',
- downsampling_voxel_size: float | None = None,
- visualization_folder: str | Path | None = None,
- num_workers: int = -1,
- eps_trunk_clustering: float = 2.5,
- min_samples_trunk_clustering: int = 1,
- min_trunk_points: int = 100,
- grid_size_canopy_height_model: float = 0.5,
- min_distance_crown_tops: float = 7,
- min_points_crown_detection: float = 100,
- min_tree_height: float = 2.5,
- smooth_canopy_height_model: bool = True,
- smooth_sigma: float = 1,
- distance_match_trunk_and_crown_top: float = 5,
- correct_watershed: bool = True,
- max_point_spacing_region_growing: float = 0.08,
- max_radius_region_growing: float = 1,
- multiplier_outside_coarse_border: float = 2,
- num_neighbors_region_growing: int = 27,
- z_scale: float = 0.5,
Bases:
InstanceSegmentationAlgorithmCoarse-to-fine algorithm for tree instance segmentation proposed in Burmeister, Josafat-Mattias, et al. “Tree Instance Segmentation in Urban 3D Point Clouds Using a Coarse-to-Fine Algorithm Based on Semantic Segmentation.” ISPRS Annals of the Photogrammetry, Remote Sensing and Spatial Information Sciences 10 (2024): 79-86. .
- Parameters:
trunk_class_id (
int) – Integer class ID that designates the tree trunk points.crown_class_id (
int) – Integer class ID that designates the tree crown points.branch_class_id (
int|None) – Integer class ID that designates the tree branch points. If set toNone, it is assumed that branch points are not separately labeled. If branches are separately labeled, the branch points are treated as trunk points by this algorithm. (default:None)algorithm (
Literal['full','watershed_crown_top_positions','watershed_matched_tree_positions']) – Variant of the algorithm to be used:"full": The full algorithm is used."watershed_crown_top_positions": A marker-controlled Watershed segmentation is performed, using the crown top positions as markers."watershed_matched_tree_positions": A marker-controlled Watershed segmentation is performed, using the tree positions as markers, resulting from the matching of crown top positions and trunk positions. (default:'full')downsampling_voxel_size (
float|None) – Voxel size for the voxel-based downsampling of the tree points before performing the tree instance segmentation. If set toNone, the tree instance segmentation is performed with the full resolution of the point cloud. (default:None)visualization_folder (
str|Path|None) – Path of a directory in which to store visualizations of intermediate results of the algorithm. If set toNone, no visualizations are stored. (default:None)num_workers (
int) – Number of workers to use for parallel processing. Ifworkersis set to -1, all CPU threads are used. (default:-1)
- Parameters for the DBSCAN clustering of trunk points:
eps_trunk_clustering – Parameter \(\epsilon\) for clustering the trunk points using the DBSCAN algorithm. Further details on the meaning of the parameter can be found in the documentation of sklearn.cluster.DBSCAN.
min_samples_trunk_clustering – Parameter \(MinSamples\) for clustering the trunk points using the DBSCAN algorithm. Further details on the meaning of the parameter can be found in the documentation of sklearn.cluster.DBSCAN.
min_trunk_points – Minimum number of points a cluster of trunk points must contain to be considered as a trunk.
- Parameters for the construction and maximum filtering of the canopy height model:
grid_size_canopy_height_model – Width of the 2D grid cells used to create the canopy height model (in meters).
min_distance_crown_tops – Minimum horizontal distance that local maxima of the canopy height model must have in order to be considered as separate crown tops (in meters).
min_points_crown_detection – Minimum number of points that must be contained in a cell of the canopy height model for the cell to be considered a possible crown top.
min_tree_height – Minimum height that a local maximum of the canopy height model must have to be considered as a crown top (in meters).
smooth_canopy_height_model – Whether to smooth the canopy height model using a Gaussian blur filter.
smooth_sigma – Parameter \(\sigma\) of the Gaussian blur filter used to smooth the canopy height model.
- Parameters for the matching of trunk positions and crown top positions:
distance_match_trunk_and_crown_top – Maximum horizontal distance between trunk positions and crown top positions up to which both are considered to belong to the same tree.
- Parameters for the Watershed segmentation:
correct_watershed – Whether erroneous parts of the watershed segmentation should be replaced by a Voronoi segmentation.
- Parameters for the region growing segmentation:
max_point_spacing_region_growing – The results of the Watershed segmentation are only refined in sufficiently dense point cloud regions. For this purpose, the average distance of the points to their nearest neighbor is determined for each tree. If this average distance is less than
max_point_spacing_region_growingthe tree is considered for refining its segmentation using the region growing approach (in meters).max_radius_region_growing – Maximum radius in which to search for neighboring points during region growing (in meters).
multiplier_outside_coarse_border – In our region growing approach, the points are processed in a sorted order, with points with the smallest distance to an already assigned tree point being processed first. For points that lie outside the crown boundary, the distance is multiplied by a constant factor to restrict growth in areas outside the crown boundary. This parameter defines this constant factor. The larger the factor, the more growth is restricted in areas outside the crown boundaries.
num_neighbors_region_growing – In our region growing approach, the k-nearest neighbors of each seed point a are searched and may be added to the same tree instance. This parameter specifies the number of neighbors to search.
z_scale – Factor by which the z-coordinates are multiplied in region growing. Using a value between zero and one favors upward growth.
- __call__(
- xyz: ndarray[tuple[Any, ...], dtype[float32 | float64]],
- classification: ndarray[tuple[Any, ...], dtype[int64]],
- point_cloud_id: str | None = None,
Segments tree instances in a point cloud.
- Parameters:
xyz (
ndarray[tuple[Any,...],dtype[float32|float64]]) – 3D coordinates of the point cloud to be segmented.classification (
ndarray[tuple[Any,...],dtype[int64]]) – Semantic segmentation class IDspoint_cloud_id (
str|None) – ID of the point cloud to be used in the file names of the visualization results. If set toNone, no visualizations are created. (default:None)
- Returns:
Instance IDs of each point. Points that do not belong to any instance are assigned the ID \(-1\).
- Shape:
xyz: \((N, 3)\)classification: \((N)\)Output: \((N)\).
where\(N = \text{ number of points}\)
- cluster_trunks(
- tree_xyz: ndarray[tuple[Any, ...], dtype[float32 | float64]],
- classification: ndarray[tuple[Any, ...], dtype[int64]],
Clusters tree trunk points using the DBSCAN algorithm and assigns the same unique instance ID to the points belonging to the same cluster.
- Parameters:
tree_xyz (
ndarray[tuple[Any,...],dtype[float32|float64]]) – Coordinates of all tree points.classification (
ndarray[tuple[Any,...],dtype[int64]]) – Class ID of all tree points.
- Returns:
- Tuple of two arrays:
Instance ID of each tree point. Points that do not belong to any instance are assigned the ID
-1.Unique instance IDs.
- Shape:
tree_xyz: \((N, 3)\)classification: \((N)\)Output: \((N)\), \((T)\)
where\(N = \text{ number of tree points}\)\(T = \text{ number of trunk instances}\)
- coarse_segmentation(
- tree_xyz: ndarray[tuple[Any, ...], dtype[float32 | float64]],
- instance_ids: ndarray[tuple[Any, ...], dtype[int64]],
- tree_positions_grid: ndarray[tuple[Any, ...], dtype[int64]],
- canopy_height_model: ndarray[tuple[Any, ...], dtype[float32 | float64]],
- grid_origin: ndarray[tuple[Any, ...], dtype[float32 | float64]],
- *,
- point_cloud_id: str | None = None,
- trunk_positions_grid: ndarray[tuple[Any, ...], dtype[int64]] | None = None,
Coarse tree instance segmentation using the marker-controlled Watershed algorithm.
- Parameters:
tree_xyz (
ndarray[tuple[Any,...],dtype[float32|float64]]) – Coordinates of all tree points.instance_ids (
ndarray[tuple[Any,...],dtype[int64]]) – Instance IDs of each tree point.tree_positions_grid (
ndarray[tuple[Any,...],dtype[int64]]) – The 2D positions of all tree instances as in integer coordinates in the grid coordinate system of the canopy height model.canopy_height_model (
ndarray[tuple[Any,...],dtype[float32|float64]]) – The canopy height model to segment.grid_origin (
ndarray[tuple[Any,...],dtype[float32|float64]]) – 2D coordinates of the origin of the canopy height model.point_cloud_id (
str|None) – ID of the point cloud to be used in the file names of the visualization results. If set toNone, no visualizations are created. (default:None)trunk_positions_grid (
ndarray[tuple[Any,...],dtype[int64]] |None) – The 2D positions of tree trunks that were not matched with a tree crown as in integer coordinates in the grid coordinate system of the canopy height model. Only used for visualization purposes. (default:None)
- Returns:
- Tuple of four arrays:
Instance ID of each tree point. Points that do not belong to any instance are assigned the ID
-1.Unique instance IDs.
2D segmentation mask with the pixels at the boundary lines between neighboring trees are assigned the background value of 0.
Segmentation mask without boundary lines.
- Shape:
tree_xyz: \((N, 3)\)instance_ids: \((N)\)tree_positions_grid: \((N, 2)\)canopy_height_model: \((W, H)\)grid_origin: \((2)\)Output: \((N)\), \((I)\), \((W, H)\), \((W, H)\)
where\(N = \text{ number of tree points}\)\(I = \text{ number of tree instances}\)\(W = \text{ extent of the canopy height model in x-direction}\)\(H = \text{ extent of the canopy height model in y-direction}\)
- compute_crown_distance_fields(
- watershed_labels_without_border: ndarray[tuple[Any, ...], dtype[int64]],
- tree_positions_grid: ndarray[tuple[Any, ...], dtype[int64]],
Calculates signed distance fields from the 2D segmentation mask of each tree. The distance values specify the 2D distance to the boundary of the segmentation mask. The distance value is negative for pixels that belong to the tree and positive for pixels that do not belong to the tree.
- Parameters:
watershed_labels_without_border (
ndarray[tuple[Any,...],dtype[int64]]) – Watershed labels without boundary lines.tree_positions_grid (
ndarray[tuple[Any,...],dtype[int64]]) – Indices of the grid cells of the canopy height model in which the tree positions are located.
- Returns:
Signed distance masks for all trees.
- Shape:
watershed_labels_without_border: \((W, H)\)tree_positions_grid: \((N, 2)\)Output: \((I, W, H)\).
where\(N = \text{ number of tree points}\)\(I = \text{ number of tree instances}\)\(W = \text{ extent of the distance fields in x-direction}\)\(H = \text{ extent of the distance fields in y-direction}\)
- compute_crown_top_positions(
- tree_xyz: ndarray[tuple[Any, ...], dtype[float32 | float64]],
- classification: ndarray[tuple[Any, ...], dtype[int64]],
Constructs a 2D canopy height model, identifies the local maxima corresponding to the crown tops and calculates their 2D position.
- Parameters:
tree_xyz (
ndarray[tuple[Any,...],dtype[float32|float64]]) – Coordinates of all tree points.classification (
ndarray[tuple[Any,...],dtype[int64]]) – Class IDs of each tree point.
- Returns:
- Tuple of four arrays:
2D position of each crown top as floating point coordinates.
Tree positions as integer coordinates in the grid coordinate system of the canopy height model.
Canopy height model
Position of the grid origin.
- Shape:
tree_xyz: \((N, 3)\)classification: \((N)\)Output: \((C, 2)\), \((C, 2)\), \((W, H)\), \((2)\).
where\(N = \text{ number of tree points}\)\(C = \text{ number of crown instances}\)\(W = \text{ extent of the canopy height model in x-direction}\)\(H = \text{ extent of the canopy height model in y-direction}\)
- compute_trunk_positions(
- tree_xyz: ndarray[tuple[Any, ...], dtype[float32 | float64]],
- instance_ids: ndarray[tuple[Any, ...], dtype[int64]],
- unique_instance_ids: ndarray[tuple[Any, ...], dtype[int64]],
Computes the 2D position of each trunk.
- Parameters:
tree_xyz (
ndarray[tuple[Any,...],dtype[float32|float64]]) – Coordinates of all tree points.instance_ids (
ndarray[tuple[Any,...],dtype[int64]]) – Instance IDs of each tree point.unique_instance_ids (
ndarray[tuple[Any,...],dtype[int64]]) – Unique instance IDs.min_points – Minimum number of points an instance must have to not be discarded.
- Returns:
2D position of each trunk.
- Shape:
tree_xyz: \((N, 3)\)instance_ids: \((N)\)unique_instance_ids: \((T)\)Output: \((T, 2)\)
where\(N = \text{ number of tree points}\)\(T = \text{ number of trunk instances}\)
- static create_height_map(
- xyz: ndarray[tuple[Any, ...], dtype[float32 | float64]],
- grid_size: float,
- bounding_box: ndarray[tuple[Any, ...], dtype[float32 | float64]] | None = None,
Creates a 2D height map from a given point cloud. For this purpose, the 3D point cloud is projected onto a 2D grid and the maximum z-coordinate within each grid cell is recorded. The value of grid cells that do not contain any point is set to zero. Before creating the height map, the point cloud is normalized by subtracting the minimum z-coordinate.
- Parameters:
xyz (
ndarray[tuple[Any,...],dtype[float32|float64]]) – Coordinates of each point.grid_size (
float) – Grid size of the height map.bounding_box (
ndarray[tuple[Any,...],dtype[float32|float64]] |None) – Bounding box coordinates specifying the area for which to compute the height map. The first element is expected to be the minimum xy-coordinate of the bounding box and the second the maximum xy-coordinate. (default:None)
- Returns:
- Tuple of three arrays:
Height map.
A map of the same size containing the number of points within each grid cell.
Position of the grid origin.
- Shape:
xyz: \((N, 3)\)bounding_box: \((2, 2)\)Output: \((W, H)\), \((W, H)\), \((2)\).
where\(N = \text{ number of points}\)\(W = \text{ extent of the height map in x-direction}\)\(H = \text{ extent of the height map in y-direction}\)
- determine_overlapping_crowns(
- tree_xyz: ndarray[tuple[Any, ...], dtype[float32 | float64]],
- classification: ndarray[tuple[Any, ...], dtype[int64]],
- instance_ids: ndarray[tuple[Any, ...], dtype[int64]],
- unique_instance_ids: ndarray[tuple[Any, ...], dtype[int64]],
- canopy_height_model: ndarray[tuple[Any, ...], dtype[float32 | float64]],
- watershed_labels_with_border: ndarray[tuple[Any, ...], dtype[int64]],
- watershed_labels_without_border: ndarray[tuple[Any, ...], dtype[int64]],
Identifies trees whose crowns overlap with neighboring trees. If such trees have sufficient point density and contain trunk points, their segmentation is refined. For this purpose, the trunk points are selected as seed points for region growing and the instance IDs of the crown points are reset to -1.
- Parameters:
tree_xyz (
ndarray[tuple[Any,...],dtype[float32|float64]]) – Coordinates of all tree points.classification (
ndarray[tuple[Any,...],dtype[int64]]) – Class ID of all tree points.instance_ids (
ndarray[tuple[Any,...],dtype[int64]]) – Instance IDs of each tree point.unique_instance_ids (
ndarray[tuple[Any,...],dtype[int64]]) – Unique instance IDs.canopy_height_model (
ndarray[tuple[Any,...],dtype[float32|float64]]) – The canopy height model.watershed_labels_with_border (
ndarray[tuple[Any,...],dtype[int64]]) – Watershed labels with boundary lines between neighboring trees are assigned the background value of 0.
- Returns:
- Tuple of two arrays:
A boolean mask indicating which points were selected as seed points for region growing.
Updated instance IDs for each tree points. For trees whose segmentation is to be refined the instance IDs of the crown points is set to
-1.
- Shape:
tree_xyz: \((N, 3)\)classification: \((N)\)instance_ids: \((N)\)unique_instance_ids: \((I)\)canopy_height_model: \((W, H)\)watershed_labels_with_border: \((W, H)\)Output: \((N)\), \((N)\).
where\(N = \text{ number of tree points}\)\(I = \text{ number of tree instances}\)\(W = \text{ extent of the canopy height model in x-direction}\)\(H = \text{ extent of the canopy height model in y-direction}\)
- grow_trees(
- tree_xyz: ndarray[tuple[Any, ...], dtype[float32 | float64]],
- instance_ids: ndarray[tuple[Any, ...], dtype[int64]],
- unique_instances_ids: ndarray[tuple[Any, ...], dtype[int64]],
- grid_origin: ndarray[tuple[Any, ...], dtype[float32 | float64]],
- crown_distance_fields: ndarray[tuple[Any, ...], dtype[float32 | float64]],
- seed_mask: ndarray[tuple[Any, ...], dtype[bool]],
Region growing segmentation.
- Parameters:
tree_xyz (
ndarray[tuple[Any,...],dtype[float32|float64]]) – Coordinates of all tree points.instance_ids (
ndarray[tuple[Any,...],dtype[int64]]) – Instance IDs of each tree point.grid_origin (
ndarray[tuple[Any,...],dtype[float32|float64]]) – 2D coordinates of the origin of the crown distance fields.crown_distance_fields (
ndarray[tuple[Any,...],dtype[float32|float64]]) – 2D signed distance fields idicating the distance to the crown boundary of each tree to segment.seed_mask (
ndarray[tuple[Any,...],dtype[bool]]) – Boolean mask indicating which points were selected as seed points for region growing.
- Returns:
Updated instance IDs.
- Shape:
tree_xyz: \((N, 3)\)instance_ids: \((N)\)grid_origin: \((2)\)crown_distance_fields: \((I', W, H)\)seed_mask: \((N)\)Output: \((N)\).
where\(N = \text{ number of tree points}\)\(I' = \text{ number of tree instances to segment}\)\(W = \text{ extent of the distance fields in x-direction}\)\(H = \text{ extent of the distance fields in y-direction}\)
- match_trunk_and_crown_tops(
- trunk_positions: ndarray[tuple[Any, ...], dtype[float32 | float64]],
- crown_top_positions: ndarray[tuple[Any, ...], dtype[float32 | float64]],
Merges trunk and crown topm positions corresponding to the same tree.
- Parameters:
trunk_positions (
ndarray[tuple[Any,...],dtype[float32|float64]]) – 2D position of each trunk.crown_top_positions (
ndarray[tuple[Any,...],dtype[float32|float64]]) – 2D position of each crown top.
- Returns:
Merged tree positions.
- Shape:
trunk_positions: \((T, 2)\)crown_top_positions: \((C, 2)\)Output: \((I, 2)\)
where\(T = \text{ number of trunk instances}\)\(C = \text{ number of crown instances}\)\(I = \text{ number of merged tree instances}\)
- watershed_correction(
- watershed_labels_with_border: ndarray[tuple[Any, ...], dtype[int64]],
- watershed_labels_without_border: ndarray[tuple[Any, ...], dtype[int64]],
- tree_positions_grid: ndarray[tuple[Any, ...], dtype[int64]],
- point_cloud_id: str | None = None,
Detects erroneous parts within a Watershed segmentation mask and replaces them by a Voronoi segmentation.
- Parameters:
watershed_labels_with_border (
ndarray[tuple[Any,...],dtype[int64]]) – Uncorrected Watershed labels with boundary lines between neighboring trees are assigned the background value of 0.watershed_labels_without_border (
ndarray[tuple[Any,...],dtype[int64]]) – Uncorrected Watershed labels without boundary lines.tree_positions_grid (
ndarray[tuple[Any,...],dtype[int64]]) – Indices of the grid cells of the canopy height model in which the tree positions are located.point_cloud_id (
str|None) – ID of the point cloud to be used in the file names of the visualization results. If set toNone, no visualizations are created. (default:None)
- Returns:
- Tuple of two arrays:
Corrected Watershed segmentation mask with the pixels at the boundary lines between neighboring trees are assigned the background value of 0.
Corrected Watershed segmentation mask without boundary lines.
- Shape:
watershed_labels_with_border: \((W, H)\)watershed_labels_without_border: \((W, H)\)tree_positions_grid: \((I, 2)\)Output: \((W, H)\), :math:`(W, H).
where\(I = \text{ number of tree instances}\)\(W = \text{ extent of the canopy height model in x-direction}\)\(H = \text{ extent of the canopy height model in y-direction}\)
- watershed_segmentation(
- canopy_height_model: ndarray[tuple[Any, ...], dtype[float32 | float64]],
- tree_positions_grid: ndarray[tuple[Any, ...], dtype[int64]],
Marker-controlled Watershed segmentation of the canopy height model.
- Parameters:
canopy_height_model (
ndarray[tuple[Any,...],dtype[float32|float64]]) – The canopy height model to segment.tree_positions_grid (
ndarray[tuple[Any,...],dtype[int64]]) – Indices of the grid cells of the canopy height model in which the tree positions are located.
- Returns:
- Tuple of two arrays:
Watershed segmentation mask with the pixels at the boundary lines between neighboring trees are assigned the background value of 0.
Watershed segmentation mask without boundary lines.
- Shape:
canopy_height_model: \((W, H)\)tree_positions_grid: \((I, 2)\)Output: Tuple of two arrays. Both have shape \((W, H)\).
where\(I = \text{ number of tree instances}\)\(W = \text{ extent of the canopy height model in x-direction}\)\(H = \text{ extent of the canopy height model in y-direction}\)
- class pointtree.instance_segmentation.InstanceSegmentationAlgorithm[source]
Bases:
ABCBase class for implementing tree instance segmentation algorithms.
- abstractmethod __call__(*args, **kwargs) Any[source]
This method should be overwritten in subclasses to implement the specific tree instance segmentation algorithm.
- performance_metrics() DataFrame[source]
- Returns:
Tracked performance metrics as pandas.DataFrame with the columns
"Description","Wallclock Time [s]","CPU Time [s]","Memory Usage [GB]", and"Memory Increment [GB]".
- class pointtree.instance_segmentation.TreeXAlgorithm(
- *,
- invalid_tree_id: int = -1,
- num_workers: int | None = -1,
- visualization_folder: str | Path | None = None,
- random_seed: int = 0,
- csf_terrain_classification_threshold: float = 0.5,
- csf_tree_classification_threshold: float = 0.5,
- csf_correct_steep_slope: bool = False,
- csf_iterations: int = 500,
- csf_resolution: float = 0.5,
- csf_rigidness: int = 2,
- dtm_k: int = 400,
- dtm_power: float = 1,
- dtm_resolution: float = 0.25,
- dtm_voxel_size: float = 0.05,
- stem_search_min_z: float = 1.0,
- stem_search_max_z: float = 4.0,
- stem_search_voxel_size: float = 0.015,
- stem_search_dbscan_2d_eps: float = 0.025,
- stem_search_dbscan_2d_min_points: int = 90,
- stem_search_dbscan_3d_eps: float = 0.1,
- stem_search_dbscan_3d_min_points: int = 15,
- stem_search_min_cluster_points: int | None = 300,
- stem_search_min_cluster_height: float | None = 1.5,
- stem_search_min_cluster_intensity: float | None = 6000,
- stem_search_pc1_min_explained_variance: float | None = None,
- stem_search_max_inclination: float | None = None,
- stem_search_refined_circle_fitting: bool = False,
- stem_search_ellipse_fitting: bool = False,
- stem_search_circle_fitting_method: Literal['m-estimator', 'ransac'] = 'ransac',
- stem_search_circle_fitting_layer_start: float = 1.0,
- stem_search_circle_fitting_num_layers: int = 15,
- stem_search_circle_fitting_layer_height: float = 0.225,
- stem_search_circle_fitting_layer_overlap: float = 0.025,
- stem_search_circle_fitting_bandwidth: float = 0.01,
- stem_search_circle_fitting_min_points: int = 15,
- stem_search_circle_fitting_min_fitting_score: float = 100.0,
- stem_search_circle_fitting_min_stem_diameter: float = 0.02,
- stem_search_circle_fitting_max_stem_diameter: float = 1.0,
- stem_search_circle_fitting_min_completeness_idx: float | None = 0.3,
- stem_search_circle_fitting_small_buffer_width: float = 0.06,
- stem_search_circle_fitting_large_buffer_width: float = 0.09,
- stem_search_circle_fitting_switch_buffer_threshold: float = 0.3,
- stem_search_ellipse_filter_threshold: float = 0.6,
- stem_search_circle_fitting_max_std_diameter: float = 0.04,
- stem_search_circle_fitting_max_std_position: float | None = None,
- stem_search_circle_fitting_std_num_layers: int = 6,
- stem_search_gam_buffer_width: float = 0.03,
- stem_search_gam_max_radius_diff: float | None = 0.3,
- tree_seg_voxel_size: float = 0.05,
- tree_seg_z_scale: float = 2,
- tree_seg_seed_layer_height: float = 0.6,
- tree_seg_seed_diameter_factor: float = 1.05,
- tree_seg_seed_min_diameter: float = 0.05,
- tree_seg_min_total_assignment_ratio: float = 0.002,
- tree_seg_min_tree_assignment_ratio: float = 0.3,
- tree_seg_max_search_radius: float = 0.5,
- tree_seg_decrease_search_radius_after_num_iter: int = 10,
- tree_seg_max_iterations: int = 500,
- tree_seg_cum_search_dist_include_terrain: float = 0.8,
Bases:
InstanceSegmentationAlgorithmRevised version of the tree instance segmentation algorithm originally introduced in the following papers:
- Parameters:
invalid_tree_id (
int) – ID that is assigned to points that do not belong to any tree instance. Must either be zero or a negative number. (default:-1)num_workers (
int|None) – Number of workers to use for parallel processing. If set to-1, all CPU threads are used. (default:-1)visualization_folder (
str|Path|None) – Path of a directory in which to store visualizations of intermediate results of the algorithm. If set toNone, no visualizations are created. (default:None)random_seed (
int) – Random seed to for reproducibility of random processes. It should be noted that even with the seed set, the algorithm is not completely deterministic, as some of the dependencies cannot be configured accordingly. (default:0)
The algorithm comprises the following steps:
1. Terrain Classification Using the CSF Algorithm
In the first step, the algorithm detects terrain points using the Cloth Simulation Filtering (CSF) algorithm proposed in Zhang, Wuming, et al. “An Easy-to-Use Airborne LiDAR Data Filtering Method Based on Cloth Simulation.” Remote Sensing 8.6 (2016): 501.
- Parameters:
csf_terrain_classification_threshold (
float) – Maximum height above the cloth a point can have in order to be classified as terrain point (in meters). All points whose distance to the cloth is equal or below this threshold are classified as terrain points. (default:0.5)csf_tree_classification_threshold (
float) – Minimum height above the cloth a point must have in order to be classified as tree point (in meters). All points whose distance to the cloth is equal or larger than this threshold are classified as tree points. (default:0.5)csf_correct_steep_slope (
bool) – Whether to include a post-processing step in the CSF algorithm that handles steep slopes. (default:False)csf_iterations (
int) – Maximum number of iterations. (default:500)csf_resolution (
float) – Resolution of the cloth grid (in meters). (default:0.5)csf_rigidness (
int) – Rigidness of the cloth (the three levels1,2, and3are available, where1is the lowest and3the highest rigidness). (default:2)
2. Construction of a Digital Terrain Model
In the next step, a rasterized digital terrain model (DTM) is constructed from the terrain points identified in the previous step. For this purpose, a grid of regularly arranged DTM points is created and the height of the \(k\) closest terrain points is interpolated to obtain the height of each DTM point on the grid. In the interpolation, the terrain height \(h(q)\) at grid position \(q\) is computed using the following formula:
\[h(q) = \frac{1}{\sum_{p \in \mathcal{N}(q, k)} w(q, p)} \cdot \sum_{p \in \mathcal{N}(q, k)} p_z \cdot w(q, p)\]where \(\mathcal{N}(q, k)\) is the set of the \(k\) terrain points closest to grid position \(q\), \(p_z\) is the z-coordinate of point \(p\), and \(w\) is an inverse-distance weighting function with a hyperparameter \(c\):
\[w(q, p) = \frac{1}{||p_{xy} - q_{xy}||^c}\]Before constructing the DTM, the terrain points are downsampled using voxel-based subsampling.
- Parameters:
dtm_k (
int) – Number of terrain points between which interpolation is performed to obtain the terrain height of a DTM point. (default:400)dtm_power (
float) – Power \(c\) for inverse-distance weighting in the interpolation of terrain points. (default:1)dtm_resolution (
float) – Resolution of the DTM grid (in meters). (default:0.25)dtm_voxel_size (
float) – Voxel size with which the terrain points are downsampled before the DTM is created (in meters). (default:0.05)
3. Detection of Tree Stems
The aim of this step is to identify clusters of points that represent individual tree stems, i.e., each stem should be represented by a single cluster. For this purpose, a horizontal layer is extracted from the point cloud that contains all points within a certain height range above the terrain (the height range is defined by
stem_search_min_zandstem_search_max_z). This layer should be chosen so that it contains all tree stems and as few other objects as possible. The points within this slice are downsampled using voxel-based subsampling and then clustered in 2D using the DBSCAN algorithm proposed in Ester, Martin, et al. “A Density-Based Algorithm for Discovering Clusters in Large Spatial Databases with Noise.” KDD. Vol. 96. No. 34, pp. 226-231. 1996. The reasoning behind this is that tree stems are usually vertical structures that form dense clusters of points when projected onto the XY plane. However, when applying the DBSCAN algorithm in 2D, stems that are located close to each other may be assigned into a single cluster. To address such cases, an additional 3D DBSCAN clustering is applied to the points within each cluster to further split the clusters.After the clustering, the following filtering rules are applied to the clusters to filter out false positive clusters that do not represent tree stems:
Clusters with less than
stem_search_min_cluster_pointspoints are discarded.Clusters whose extent in the z-direction (i.e., the height difference between the highest and the lowest point in the cluster) is less than
stem_search_min_cluster_heightare discarded.If reflection intensity values are provided in the input, the 80% quantile of the reflection intensities of the points in a cluster is calculated. Clusters that have an 80% quantile of intensities equal to or smaller than
stem_search_min_cluster_intensityare discarded.A principal component analysis is performed on the points in each cluster. Clusters are discarded if the first principal component explains less than
stem_search_pc1_min_explained_varianceof the variance or the angle between the z-axis and the first principal component is greater thanstem_search_max_inclination.From the remaining clusters,
stem_search_circle_fitting_num_layershorizontal layers are extracted. Each layer has a height ofstem_search_circle_fitting_layer_heightand overlaps with adjacent layers bystem_search_circle_fitting_layer_overlap. Within each layer, a circle and an ellipse are fitted to the contained points. For all possible combinations ofstem_search_circle_fitting_std_num_layerslayers, the standard deviation of the fitted circle diameters is computed. If any combination yields a standard deviation less than or equal tostem_search_circle_fitting_max_std_diameter, the cluster is retained. If no such combination is found, the same procedure is repeated using the diameters of the fitted ellipses. If none of these combinations satisfy the diameter standard deviation threshold, the cluster is discarded. Additionally, ifstem_search_circle_fitting_max_std_positionis notNone, the filtering also requires that the standard deviation of the x- and y-coordinates of the circle or ellipse centers of the layer combination does not exceed the given threshold in order for the cluster to be kept. Ifstem_search_ellipse_fittingis set toFalse, ellipse fitting is skipped, and only the fitted circles are used for filtering. Ifstem_search_refined_circle_fittingis set toTrue, a multi-stage fitting process is applied for circle and ellipse fitting: Initially, circles / ellipses are fitted to the layers extracted from the point clusters, which lack full point density due to the initial downsampling (this initial fitting step is also performed whenstem_search_refined_circle_fittingisFalse). Subsequently, each circle or ellipse is re-fitted using the points from a clipping region around the outline of the initially fitted circle / ellipse. A buffer region is created around this outline, with the buffer width determined as follows: if the preliminary circle / ellipse diameter is less than or equal tostem_search_circle_fitting_switch_buffer_threshold, the width is set tostem_search_circle_fitting_small_buffer_width; otherwise, it is set tostem_search_circle_fitting_large_buffer_width. All points from the full-resoultion point cloud that lie within the buffer area are collected, and the circle or ellipse is re-fitted using these points.
- Parameters:
stem_search_min_z (
float) – Height above the terrain at which the horizontal layer begins that is considered for stem detection (in meters). (default:1.0)stem_search_max_z (
float) – Height above the terrain at which the horizontal layer ends that is considered for stem detection (in meters). (default:4.0)stem_search_voxel_size (
float) – Voxel size with which the points are downsampled before performing the stem detection (in meters). (default:0.015)stem_search_dbscan_2d_eps (
float) – Parameter \(\epsilon\) of the DBSCAN algorithm for the initial clustering in 2D. The parameter defines the radius of the circular neighborhood that is used to determine the number of neighbors for a given point (in meters). (default:0.025)stem_search_dbscan_2d_min_points (
int) – Parameter \(MinPnts\) of the DBSCAN algorithm for the initial clustering in 2D. The parameter defines the number of neighbors a given point must have in order to be considered as a core point. All neighbors of a core point are added to the clusters and then checked whether they are core points themselves. (default:90)stem_search_dbscan_3d_eps (
float) – Parameter \(\epsilon\) of the DBSCAN algorithm for the clustering in 3D (in meters). (default:0.1)stem_search_dbscan_3d_min_points (
int) – Parameter \(MinPnts\) of the DBSCAN algorithm for the clustering in 3D. (default:15)stem_search_min_cluster_points (
int|None) – Minimum number of points a cluster must contain in order not to be discarded. (default:300)stem_search_min_cluster_height (
float|None) – Minimum extent in the z-direction (i.e., the height difference between the highest and the lowest point in the cluster) a cluster must have in order not to be discarded (in meters). (default:1.5)stem_search_min_cluster_intensity (
float|None) – Threshold for filtering of clusters based on reflection intensity values. Clusters are discarded if the 80 % percentile of the reflection intensities of the points in the cluster is below the given threshold. If no reflection intensity values are input to the algorithm, the intensity-based filtering is skipped. (default:6000)stem_search_pc1_min_explained_variance (
float|None) – Minimum percentage of variance that the first principal component of a cluster must explain in order to not be discarded (must be a value between zero and one). (default:None)stem_search_max_inclination (
float|None) – Maximum inclination angle to the z-axis that the first principal component of a cluster can have in order to not be discarded (in degrees). (default:None)stem_search_refined_circle_fitting (
bool) – Whether the step for the refined circle / ellipse fitting should be executed. (default:False)stem_search_ellipse_fitting (
bool) – Whether the ellipse fitting should be executed. (default:False)stem_search_circle_fitting_method (
Literal['m-estimator','ransac']) – Circle fitting method to use:"m-estimator"|"ransac". (default:'ransac')stem_search_circle_fitting_num_layers (
int) – Number of horizontal layers used for the circle / ellipse fitting. Depending on the settings forstem_search_circle_fitting_layer_heightandstem_search_circle_fitting_layer_overlap, this parameter controls which height range of the stem clusters is considered for circle / ellipse fitting. (default:15)stem_search_circle_fitting_layer_start (
float) – Height above the ground at which the lowest layer used for circle / ellipse fitting starts (in meters). (default:1.0)stem_search_circle_fitting_layer_height (
float) – Height of the horizontal layers used for circle / ellipse fitting (in meters). (default:0.225)stem_search_circle_fitting_layer_overlap (
float) – Overlap between adjacent horizontal layers used for circle / ellipse fitting (in meters). (default:0.025)stem_search_circle_fitting_bandwidth (
float) – Bandwidth for circle fitting. It is used in the calculation of the goodness of fit and the circumferential completeness index and determines how far points may be from the outline of a circle to be counted as belonging to the outline. When calculating the goodness of fit, a Gaussian kernel is used to measure the contribution of a point to the outline of the circle, and the bandwidth of the kernel is set to the specified value (in meters). (default:0.01)stem_search_circle_fitting_min_points (
int) – Minimum number of points that a horizontal layer must contain in order to perform circle / ellipse fitting on it. (default:15)stem_search_circle_fitting_min_fitting_score (
float) – Minimum fitting score that circles must achieve in the circle fitting procedure. (default:100.0)stem_search_circle_fitting_min_stem_diameter (
float) – Minimum circle / ellipse diameter to be considered a valid fit. (default:0.02)stem_search_circle_fitting_max_stem_diameter (
float) – Maximum circle / ellipse diameter to be considered a valid fit. (default:1.0)stem_search_circle_fitting_min_completeness_idx (
float|None) – Minimum circumferential completeness index that circles must achieve in the circle fitting procedure. If set toNone, circles are not filtered based on their circumferential completeness index. (default:0.3)stem_search_circle_fitting_small_buffer_width (
float) – This parameter is only used whenstem_search_refined_circle_fittingis set toTrue. It defines the width of the buffer area if the diameter of the initally fited circles or ellipses is less than or equal tostem_search_circle_fitting_switch_buffer_threshold(in meters). (default:0.06)stem_search_circle_fitting_large_buffer_width (
float) – This parameter is only used whenstem_search_refined_circle_fittingis set toTrue. It defines the width of the buffer area if the diameter of the initally fited circles or ellipses is larger thanstem_search_circle_fitting_switch_buffer_threshold(in meters). (default:0.09)stem_search_circle_fitting_switch_buffer_threshold (
float) – Threshold for the diameter of the preliminary circles or ellipses that controls when to switch betweenstem_search_circle_fitting_small_buffer_widthandstem_search_circle_fitting_large_buffer_width(in meters). This parameter is only used whenstem_search_refined_circle_fittingis set toTrue. (default:0.3)stem_search_ellipse_filter_threshold (
float) – In the ellipse fitting, ellipses are only kept if the ratio of the radius along the semi-minor axis to the radius along the semi-major axis is greater than or equal to this threshold. This parameter is only used whenstem_search_ellipse_fittingis set toTrue. (default:0.6)stem_search_circle_fitting_max_std_diameter (
float) – Threshold for filtering the stem clusters based on the standard deviation of the diameters of the fitted circles / ellipses. If there is at least one combination ofstem_search_circle_fitting_std_num_layerslayers for which the standard deviation of the diameters of the fitted circles / ellipses is below or equal to this threshold, the cluster is kept, otherwise it is discarded. Ifstem_search_circle_fitting_max_std_positionis notNone, it is additionally required that the standard deviation of the circle / ellipse center positions is smaller than or equal to the given threshold for the combination of layers. (default:0.04)stem_search_circle_fitting_max_std_position (
float|None) – Threshold for filtering the stem clusters based on the standard deviation of the center positions of the fitted circles / ellipses. Requires that a combination ofstem_search_circle_fitting_std_num_layerslayers must exist for which the standard deviation of the center positions of the fitted circles / ellipses is below or equal to this threshold in order to keep a cluster. If set toNone, this filtering criterion is deactivated. (default:None)stem_search_circle_fitting_std_num_layers (
int) – Number of horizontal layers to consider in each sample for calculating the standard deviation of the diameters / center positions of the fitted circles / ellipses. (default:6)
4. Computation of Stem Positions and Diameters
In this step, the stem clusters obtained in the previous step are used to compute the stem positions and stem diameters at breast height. For this purpose, the circles and ellipses fitted in the previous step are used. For each stem, the combination of those circles or ellipses (if no valid combination of circles was found) is selected whose diameters have the lowest standard deviation. To estimate the stem position at breast height, a linear model is fitted that predicts the center position of the selected circles or ellipses from the layer height. The prediction of the fitted model for a height of 1.3 m is used as an estimate of the stem position at breast height.
To estimate the stem diameter at breast height, the stem radius for each of the selected layers is re-estimated by fitting a generalized additive model (GAM) to the points from the respective layer. If
stem_search_refined_circle_fittingisTrue, only the points within the clipping area created for the refined circle / ellipse fitting are used for the GAM fitting. Otherwise, a clipping area with a width ofstem_search_gam_buffer_widthis created around the circle / ellipse outlines to select the input points for the GAM fitting. Before fitting the GAM, the input points are centered around the circle / ellipse center and converted into polar coordinates. The GAM is then used to predict the radius of the polar coordinates from the angle. The fitted GAM is then used to predict the stem radii in one-degree intervals. From these predictions, the stem’s boundary polygon is constructed and the stem diameter is estimated from the area of the boundary polygon. Finally, a linear model is fitted that predicts the stem diameter from the layer height. The prediction of the fitted model for a height of 1.3 m is used as an estimate of the stem diameter at breast height.- Parameters:
stem_search_gam_buffer_width (
float) – If the refined circle fitting is deactivated (i.e.,stem_search_refined_circle_fittingis set toFalse), all points in a buffer area around the outline of the initial circle / ellipse are cut out for the GAM fitting. This parameter defines the width of the buffer area. (default:0.03)stem_search_gam_max_radius_diff (
float|None) – If the difference between the minimum and the maximum of predicted radii is greater than this parameter, the fitted GAM is considered invalid, and the diameter of the fitted circle / ellipse is used instead. (default:0.3)
5. Tree Segmentation Using Region Growing
This stage aims to determine the complete sets of points that represent each tree. In particular, this involves segmenting the canopy points into individual tree crowns. To accomplish this, a region growing method is used. Before the region growing, the points are downsampled using voxel-based subsampling. In the first step, the region growing procedure selects an initial set of seed points for each tree. These should be points that are very likely to belong to the corresponding tree. In an iterative process, the sets of points assigned to each tree are then expanded. In each iteration, the neighboring points of each seed point within a certain search radius are determined. Neighboring points that are not yet assigned to any tree are added to the same tree as the seed point and become seed points in the next iteration. The region growing continues until there are no more seed points to be processed or the maximum number of iterations is reached.
To select the initial seed points for a given tree, the following approach is used: (1) All points that were assigned to the respective stem during the stem detection stage are used as seed points. (2) Additionally, a cylinder with a height of
tree_seg_seed_layer_heightand a diameter oftree_seg_seed_diameter_factor * dis considered, wheredis the tree’s stem diameter at breast height, which has been computed in the previous step. The cylinder’s center is positioned at the stem center at breast height, which also has been computed in the previous stage. All points within the cylinder that have not yet been selected as seed points for other trees are selected as seed points.The search radius for the iterative region growing procedure is set as follows: First, the search radius is set to the voxel size used for voxel-based subsampling, which is done before starting the region growing procedure. The search radius is increased by the voxel size if one of the following conditions is fulfilled at the end of a region growing iteration:
The ratio between the number of points newly assigned to trees in the iteration and the number of remaining, unassigned points is below
tree_seg_min_total_assignment_ratio.The ratio between the number of trees to which new points have been assigned in the iteration and the total number of trees is below
tree_seg_min_tree_assignment_ratio.
The search radius is increased up to a maximum radius of
tree_seg_max_search_radius. If the search radius has not been increased fortree_seg_decrease_search_radius_after_num_iter, it is reduced by the voxel size.To promote upward growth, the z-coordinates of the points are divided by
tree_seg_z_scalebefore the region growing.Since the terrain filtering in the first step of the algorithm may be inaccurate and some tree points may be falsely classified as terrain points, both terrain and non-terrain points are considered by the region growing procedure. However, to prevent large portions of terrain points from being included in tree instances, terrain points are only assigned to a tree if their cumulative search distance from the initial seed point is below the threshold defined by
tree_seg_cum_search_dist_include_ground. The cumulative search distance is defined as the total distance traveled between consecutive points until reaching a terrain point.- Parameters:
tree_seg_voxel_size (
float) – Voxel size with which the points are downsampled before the region growing (in meters). (default:0.05)tree_seg_z_scale (
float) – Factor by which to divide the z-coordinates of the points before the region growing. To promote upward growth, this factor should be larger than 1. (default:2)tree_seg_seed_layer_height (
float) – Height of the cylinders that are placed around the stem centers at breast height for seed point selection (in meters). (default:0.6)tree_seg_seed_diameter_factor (
float) – Factor to multiply with the stem diameter at breast height to obtain the diameter of the cylinder used for seed point selection. (default:1.05)tree_seg_seed_min_diameter (
float) – Minimum diameter of the cylinder used for seed point selection. (default:0.05)tree_seg_min_total_assignment_ratio (
float) – Threshold controlling when to increase the search radius. If the ratio between the number of points newly assigned to trees in an iteration and the number of remaining, unassigned points is below this threshold, the search radius is increased bytree_seg_voxel_sizeup to a maximum search radius oftree_seg_max_search_radius. (default:0.002)tree_seg_min_tree_assignment_ratio (
float) – Threshold controlling when to increase the search radius. If the ratio between the number of trees to which new points have been assigned in the iteration and the total number of trees is below this threshold, the search radius is increased bytree_seg_voxel_sizeup to a maximum search radius oftree_seg_max_search_radius. (default:0.3)tree_seg_max_search_radius (
float) – Maximum search radius (in meters). (default:0.5)tree_seg_decrease_search_radius_after_num_iter (
int) – Number of region growing iterations after which to decrease the search radius bytree_seg_voxel_sizeif it has not been increased in these iterations. (default:10)tree_seg_max_iterations (
int) – Maximum number of region growing iterations. (default:500)tree_seg_cum_search_dist_include_terrain (
float) – Maximum cumulative search distance between the initial seed point and a terrain point to include that terrain point in a tree instance (in meters). (default:0.8)
- Raises:
ValueError – If
stem_search_min_zis set to a value smaller thancsf_tree_classification_threshold.ValueError – If
stem_search_circle_fitting_layer_startis set to a value smaller thanstem_search_min_z.ValueError – If
invalid_tree_idis set to a value greater than one.ValueError – If
stem_search_circle_fitting_min_stem_diameteris greater than or equal tostem_search_circle_fitting_max_stem_diameter.
- __call__(
- xyz: ndarray[tuple[Any, ...], dtype[float32 | float64]],
- intensities: ndarray[tuple[Any, ...], dtype[float32 | float64]] | None = None,
- point_cloud_id: str | None = None,
- crs: str | None = None,
Runs the tree instance segmentation for the given point cloud.
- Parameters:
xyz (
ndarray[tuple[Any,...],dtype[float32|float64]]) – 3D coordinates of all points in the point cloud.intensities (
ndarray[tuple[Any,...],dtype[float32|float64]] |None) – Reflection intensities of all points in the point cloud. If set toNone, filtering steps that use intensity values are skipped. (default:None)point_cloud_id (
str|None) – ID of the point cloud to be used in the file names of the visualization results. If set toNone, no visualizations are created. (default:None)crs (
str|None) – EPSG code of the coordinate reference system of the input point cloud. The EPSG code is used to set the coordinate reference system when exporting intermediate data, such as a digital terrain model file. If set toNone, no coordinate reference system is set for the exported data. (default:None)
- Returns:
- Tuple of three arrays:
Tree instance labels for all points. For points not belonging to any tree, the label is set to
invalid_instance_id(constructor parameter).Stem positions of the detected trees (xy-coordinates of the stem center at breast height).
Stem diameters at breast height of the detected trees.
- Raises:
ValueError – If
intensitiesis notNone.ValueError – If
xyzandintensitieshave different lengths.
- Shape:
xyz: \((N, 3)\)intensities: \((N)\)Output: \((N)\), \((T)\), \((T)\)
where\(N = \text{ number of points}\)\(T = \text{ number of detected trees}\)
Example:
from pointtree.instance_segmentation import TreeXAlgorithm from pointtorch import read point_cloud = read("path to point cloud file") algorithm = TreeXAlgorithm() xyz = point_cloud.xyz() intensities = point_cloud["intensity"].to_numpy() instance_ids, stem_positions, stem_diameters = algorithm(xyz, intensities)
- compute_stem_diameters(
- layer_circles: ndarray[tuple[Any, ...], dtype[float32 | float64]],
- layer_ellipses: ndarray[tuple[Any, ...], dtype[float32 | float64]],
- layer_heights: ndarray[tuple[Any, ...], dtype[float32 | float64]],
- stem_layer_xy: ndarray[tuple[Any, ...], dtype[float32 | float64]],
- batch_lengths_xy: ndarray[tuple[Any, ...], dtype[int64]],
- best_circle_combination: ndarray[tuple[Any, ...], dtype[int64]],
- best_ellipse_combination: ndarray[tuple[Any, ...], dtype[int64]],
- *,
- point_cloud_id: str | None = None,
Calculates the stem diameters using the circles or ellipses fitted to multiple horizontal layers of the stems. For this purpose, the combination of
stem_search_circle_fitting_std_num_layers(constructor parameter) circles or ellipses with the smallest standard deviation of the diameters is selected. The stem diameter for each selected layer is computed by fitting a GAM to the points of that layer, using the centers of the previously fitted circles or ellipses to normalize the points. A linear model is then fitted to the stem diameters obtained from the GAM to predict the stem diameter as a function of the height above the ground. The prediction of the linear model for a height of 1.3 m above the ground is returned as an estimate of the stem diameter at breast height.- Parameters:
layer_circles (
ndarray[tuple[Any,...],dtype[float32|float64]]) – Parameters of the circles that were fitted to the horizontal layers of each cluster. Each circle must be represented by three values, namely the x- and y-coordinates of its center and its radius. If no circle was found for a certain layer, the circle parameters for that layer must be set to-1.layer_ellipses (
ndarray[tuple[Any,...],dtype[float32|float64]]) – Parameters of the ellipses that were fitted to the horizontal layers of each cluster. Each ellipse must represented by five values, namely the x- and y-coordinates of its center, its radius along the semi-major and along the semi-minor axis, and the counterclockwise angle of rotation from the x-axis to the semi-major axis of the ellipse. If no ellipse was found for a certain layer, the ellipse parameters for that layer must be set to-1.layer_heights (
ndarray[tuple[Any,...],dtype[float32|float64]]) – Heights above the ground of the midpoints of the horizontal layers to which the circles or ellipses were fitted.stem_layer_xy (
ndarray[tuple[Any,...],dtype[float32|float64]]) – X- and y-coordinates of the points belonging to the different horizontal layers of the stems. Points that belong to the same layer of the same stem must be stored consecutively and the number of points belonging to each layer must be specified usingbatch_lengths_xy.batch_lengths_xy (
ndarray[tuple[Any,...],dtype[int64]]) – Number of points belonging to each horizontal layer of each stem.best_circle_combination (
ndarray[tuple[Any,...],dtype[int64]]) – Indices of the combination of layers with the lowest standard deviation of the circle diameters for each stem cluster. If no valid combination of circles was found for a stem cluster, the indices for that cluster must be set to-1.best_ellipse_combination (
ndarray[tuple[Any,...],dtype[int64]]) – Indices of the combination of layers with the lowest standard deviation of the ellipse diameters for each cluster. If a valid combination of circles was found for a stem cluster, the ellipses are not considered for calculating the stem position. If no valid combination of ellipse was found for a stem cluster, the indices for that cluster must be set to-1.point_cloud_id (
str|None) – ID of the point cloud to be used in the file names of the visualization results. If set toNone, no visualizations are created. (default:None)
- Returns:
Diameters at breast height of each stem (1.3 m).
- Shape:
layer_circles: \((S, L, 3)\)layer_ellipses: \((S, L, 5)\)layer_heights: \((L)\)stem_layer_xy: \((N, 2)\)batch_lengths_xy: \((S \cdot L)\)best_circle_combination: \((S, L')\)best_ellipse_combination: \((S, L')\)Output: \((S)\).
where\(N = \text{ number of points in the stem layer}\)\(S = \text{ number of detected stems}\)\(L = \text{ number of horinzontal layers to which circles and ellipses are fitted}\)\(L' = \text{ number of horinzontal layers considered for filtering}\)
- compute_stem_positions(
- layer_circles: ndarray[tuple[Any, ...], dtype[float32 | float64]],
- layer_ellipses: ndarray[tuple[Any, ...], dtype[float32 | float64]],
- layer_heights: ndarray[tuple[Any, ...], dtype[float32 | float64]],
- best_circle_combination: ndarray[tuple[Any, ...], dtype[int64]],
- best_ellipse_combination: ndarray[tuple[Any, ...], dtype[int64]],
Calculates the stem positions using the circles or ellipses fitted to multiple horizontal layers of the stems. For this purpose, the combination of
stem_search_circle_fitting_std_num_layers(constructor parameter) circles or ellipses with the smallest standard deviation of the diameters is selected. A linear model is fitted to these circles or ellipses to predict the centers of the circles or ellipses as a function of the height above the ground. The prediction of the linear model for a height of 1.3 m above the ground is returned as am estimate of the stem position at breast height.- Parameters:
layer_circles (
ndarray[tuple[Any,...],dtype[float32|float64]]) – Parameters of the circles that were fitted to the horizontal layers of each cluster. Each circle must be represented by three values, namely the x- and y-coordinates of its center and its radius. If no circle was found for a certain layer, the circle parameters for that layer must be set to-1.layer_ellipses (
ndarray[tuple[Any,...],dtype[float32|float64]]) – Parameters of the ellipses that were fitted to the horizontal layers of each cluster. Each ellipse must represented by five values, namely the x- and y-coordinates of its center, its radius along the semi-major and along the semi-minor axis, and the counterclockwise angle of rotation from the x-axis to the semi-major axis of the ellipse. If no ellipse was found for a certain layer, the ellipse parameters for that layer must be set to-1.layer_heights (
ndarray[tuple[Any,...],dtype[float32|float64]]) – Heights above the ground of the midpoints of the horizontal layers to which the circles or ellipses were fitted.best_circle_combination (
ndarray[tuple[Any,...],dtype[int64]]) – Indices of the combination of layers with the lowest standard deviation of the circle diameters for each stem cluster. If less thanstem_search_circle_fitting_std_num_layers(constructor parameter) circles were found for a stem cluster, the indices for that cluster must be set to-1.best_ellipse_combination (
ndarray[tuple[Any,...],dtype[int64]]) – Indices of the combination of layers with the lowest standard deviation of the ellipse diameters for each cluster. If more thanstem_search_circle_fitting_std_num_layers(constructor parameter) circles were found for a stem cluster, the ellipses are not considered for calculating the stem position.
- Returns:
X- and y-coordinates of the position of each stem at breast height (1.3 m).
- Shape:
layer_circles: \((S, L, 3)\)layer_ellipses: \((S, L, 5)\)layer_heights: \((L)\)best_circle_combination: \((S, L')\)best_ellipse_combination: \((S, L')\)Output: \((S, 2)\).
where\(S = \text{ number of detected stems}\)\(L = \text{ number of horinzontal layers to which circles and ellipses are fitted}\)\(L' = \text{ number of horinzontal layers considered for filtering}\)
- detect_stems(
- stem_layer_xyz: ndarray[tuple[Any, ...], dtype[float32 | float64]],
- dtm: ndarray[tuple[Any, ...], dtype[float32 | float64]],
- dtm_offset: ndarray[tuple[Any, ...], dtype[float32 | float64]],
- intensities: ndarray[tuple[Any, ...], dtype[_ScalarT]] | None = None,
- point_cloud_id: str | None = None,
- crs: str | None = None,
Detects tree stems in a 3D point cloud.
- Parameters:
stem_layer_xyz (
ndarray[tuple[Any,...],dtype[float32|float64]]) – Point coordinates of the points within the stem layer as defined by the constructor parametersstem_search_min_zandstem_search_max_z.dtm (
ndarray[tuple[Any,...],dtype[float32|float64]]) – Digital terrain model.dtm_offset (
ndarray[tuple[Any,...],dtype[float32|float64]]) – X- and y-coordinate of the top left corner of the DTM grid.intensities (
ndarray[tuple[Any,...],dtype[TypeVar(_ScalarT, bound=generic)]] |None) – Reflection intensities of all points in the point cloud. If set toNone, filtering steps that use intensity values are skipped. (default:None)point_cloud_id (
str|None) – ID of the point cloud to be used in the file names of the visualization results. If set toNone, no visualizations are created. (default:None)crs (
str|None) – EPSG code of the coordinate reference system of the input point cloud. The EPSG code is used to set the coordinate reference system when exporting intermediate data. If set toNone, no coordinate reference system is set for the exported data. (default:None)
- Returns:
- Tuple of three arrays:
X- and y-coordinates of the position of each detected stem at breast height (1.3 m).
Diameter of each detected stem at breast height.
Stem ID label of each point. Points that do not belong to any stem are assigned the label
-1.
- Shape:
stem_layer_xyz: \((N, 3)\)dtm: \((H, W)\)dtm_offset: \((2)\)intensities: \((N)\)Output: \((S, 2)\), \((S)\), \((N)\).
where\(N = \text{ number of points}\)\(S = \text{ number of detected stems}\)\(H = \text{ extent of the DTM grid in y-direction}\)\(W = \text{ extent of the DTM grid in x-direction}\)
- export_dtm(
- dtm: ndarray[tuple[Any, ...], dtype[float32 | float64]],
- dtm_offset: ndarray[tuple[Any, ...], dtype[float32 | float64]],
- point_cloud_id: str,
- crs: str | None = None,
Exports the given digital terrain model as a GeoTIF file to
visualization_folder(constructor parameter).- Parameters:
dtm (
ndarray[tuple[Any,...],dtype[float32|float64]]) – Digital terrain model.dtm_offset (
ndarray[tuple[Any,...],dtype[float32|float64]]) – X- and y-coordinate of the top left corner of the DTM grid.point_cloud_id (
str) – ID of the point cloud to be used in the file name.crs (
str|None) – Coordinate reference system to be used. If set toNone, the output file is not georeferenced. (default:None)
- Raises:
ValueError – If
visualization_folderisNone.
- Shape:
dtm: \((H, W)\)dtm_offset: \((2)\)
where\(H = \text{ extent of the DTM grid in y-direction}\)\(W = \text{ extent of the DTM grid in x-direction}\)
- export_point_cloud(
- xyz: ndarray[tuple[Any, ...], dtype[float32 | float64]],
- attributes: Dict[str, ndarray[tuple[Any, ...], dtype[_ScalarT]]],
- step_name: str,
- point_cloud_id: str,
- crs: str | None = None,
Exports a point cloud representing intermediate results as LAZ file to
visualization_folder(constructor parameter).- Parameters:
xyz (
ndarray[tuple[Any,...],dtype[float32|float64]]) – Point coordinates of the points.attributes (
Dict[str,ndarray[tuple[Any,...],dtype[TypeVar(_ScalarT, bound=generic)]]]) – Dictionary with additional per-point attributes. The keys of the dictionary are the attribute names and the corresponding values must be numpy arrays of the same length asxyz.step_name (
str) – Name of the processing step to be included in the file name.point_cloud_id (
str) – ID of the point cloud to be used in the file name.crs (
str|None) – Coordinate reference system to be used. If set toNone, the output file is not georeferenced. (default:None)
- Raises:
ValueError – If
visualization_folderisNone.ValueError – If the values in
attributeshave a different length thanxyz.
- Shape:
xyz: \((N, 3)\)attributes: each value must have shape \((N)\)
where\(N = \text{ number of points}\)
- filter_instances_stem_layers_std(
- layer_circles: ndarray[tuple[Any, ...], dtype[float32 | float64]],
- layer_ellipses: ndarray[tuple[Any, ...], dtype[float32 | float64]],
Filters the point clusters that may represent individual tree stems based on the circles and ellipses fitted to different horizontal layers of the clusters. For each cluster, the standard deviation of the fitted circle or ellipse diameters is computed for all possible combinations of
stem_search_circle_fitting_std_num_layerslayers. If for any of the combinations the standard deviation of the circle diameters is smaller than or equal tostem_search_circle_fitting_max_std_diameter, the cluster is kept. Otherwise, it is checked if for any of the combinations the standard deviation of the ellipse diameters is smaller than or equal tostem_search_circle_fitting_max_std_diameter. If that is also not the case, the cluster is discarded.If
stem_search_circle_fitting_max_std_positionis notNone, the standard deviation of the x- and y-coordinates of the circle / ellipse centers of the selected combination of layers must be smaller than or equal to the given threshold to keep the stem cluster (the variables are constructor parameters).- Parameters:
layer_circles (
ndarray[tuple[Any,...],dtype[float32|float64]]) – Parameters of the circles that were fitted to the horizontal layers of each cluster. Each circle must be represented by three values, namely the x- and y-coordinates of its center and its radius. If no circle was found for a certain layer, the circle parameters for that layer must be set to-1.layer_ellipses (
ndarray[tuple[Any,...],dtype[float32|float64]]) – Parameters of the ellipses that were fitted to the horizontal layers of each cluster. Each ellipse must represented by five values, namely the x- and y-coordinates of its center, its radius along the semi-major and along the semi-minor axis, and the counterclockwise angle of rotation from the x-axis to the semi-major axis of the ellipse. If no ellipse was found for a certain layer, the ellipse parameters for that layer must be set to-1.
- Returns:
- Tuple of three arrays:
Boolean mask indicating which stem clusters are retained after the filtering.
Indices of the combination of layers with the lowest standard deviation of the circle diameters for each cluster. If for a stem cluster, no combination of layers was found for which the fitted circles fulfill the filtering criteria, the indices for that cluster are set to
-1.Indices of the combination of layers with the lowest standard deviation of the ellipse diameters for each cluster. If for a stem cluster, no combination of layers was found for which the fitted ellipses fulfill the filtering criteria, the indices for that cluster are set to
-1.
- Shape:
layer_circles: \((S, L, 3)\)layer_ellipses: \((S, L, 5)\)Output: \((S)\), \((S', L')\), \((S', L')\).
where\(S = \text{ number of stem clusters before the filtering}\)\(S' = \text{ number of stem clusters after the filtering}\)\(L = \text{ number of horinzontal layers to which circles and ellipses are fitted}\)\(L' = \text{ number of horinzontal layers considered for filtering}\)
- fit_circles_or_ellipses_to_stems(
- stem_layer_xyz: ndarray[tuple[Any, ...], dtype[float32 | float64]],
- cluster_labels: ndarray[tuple[Any, ...], dtype[int64]],
- unique_cluster_labels: ndarray[tuple[Any, ...], dtype[int64]],
- dtm: ndarray[tuple[Any, ...], dtype[float32 | float64]],
- dtm_offset: ndarray[tuple[Any, ...], dtype[float32 | float64]],
- point_cloud_id: str | None = None,
Given a set of point clusters that may represent individual tree stems, circles and ellipses are fitted to multiple horinzontal layers of each cluster. If a horizontal layer contains less than
stem_search_circle_fitting_min_pointspoints, neither a circle nor an ellipse is fitted. To obtain the horizontal layers,stem_search_circle_fitting_num_layershorizontal layers with a height ofstem_search_circle_fitting_layer_heightare created starting at a height ofstem_search_circle_fitting_layer_start. The layers have an overlap ofstem_search_circle_fitting_layer_overlapto the previous layer (the variables are constructor parameters).The ellipse fitting is only done if
stem_search_ellipse_fitting(constructor parameter) is set toTrue.- Parameters:
stem_layer_xyz (
ndarray[tuple[Any,...],dtype[float32|float64]]) – Point coordinates of the points within the stem layer as defined by the constructor parametersstem_search_min_zandstem_search_max_z.cluster_labels (
ndarray[tuple[Any,...],dtype[int64]]) – Indices indicating to which cluster each point belongs. Points not belonging to any cluster should be assigned the ID-1.unique_cluster_labels (
ndarray[tuple[Any,...],dtype[int64]]) – Unique cluster labels, i.e., an array that should contain each cluster ID once. The cluster IDs are expected to start with zero and to be in a continuous range.dtm (
ndarray[tuple[Any,...],dtype[float32|float64]]) – Digital terrain model.dtm_offset (
ndarray[tuple[Any,...],dtype[float32|float64]]) – X- and y-coordinate of the top left corner of the DTM grid.point_cloud_id (
str|None) – ID of the point cloud to be used in the file names of the visualization results. If set toNone, no visualizations are created. (default:None)
- Returns:
- Tuple of six arrays:
Parameters of the circles that were fitted to the layers of each cluster. Each circle is represented by three values, namely the x- and y-coordinates of its center and its radius. If the circle fitting is not successfull for a layer, all parameters are set to
-1.Parameters of the ellipses that were fitted to the layers of each cluster. Each ellipse is represented by five values, namely the x- and y-coordinates of its center, its radius along the semi-major and along the semi-minor axis, and the counterclockwise angle of rotation from the x-axis to the semi-major axis of the ellipse. If the ellipse fitting is not successfull for a layer or results in an ellipse whose axis ratio is smaller than
stem_search_ellipse_filter_threshold(constructor parameter), all parameters are set to-1.Terrain height at the centroid position of each stem cluster.
Height above the terrain of the midpoint of each horizontal layer.
X- and y-coordinates of the points in each horizontal layer of each cluster. Points belonging to the same layer of the same cluster are stored consecutively.
Number of points belonging to each horizontal layer of each cluster.
- Shape:
stem_layer_xyz: \((N, 3)\)cluster_labels: \((N)\)unique_cluster_labels: \((S)\)Output: \((S, L, 3)\), \((S, L, 5)\), \((S)\), \((L)\), \((N_{0,0} + ... + N_{S, L}, 2)\), \((S \cdot L)\)
where\(N = \text{ number of points in the stem layer}\)\(S = \text{ number of stem clusters}\)\(L = \text{ number of horinzontal layers to which circles / ellipses are fitted}\)\(N_{t,s} = \text{ number of points selected in the l-th layer of cluster } s\)
- fit_refined_circles_and_ellipses_to_stems(
- stem_layer_xyz: ndarray[tuple[Any, ...], dtype[float32 | float64]],
- preliminary_layer_circles: ndarray[tuple[Any, ...], dtype[float32 | float64]],
- preliminary_layer_ellipses: ndarray[tuple[Any, ...], dtype[float32 | float64]],
- terrain_heights_at_cluster_positions: ndarray[tuple[Any, ...], dtype[float32 | float64]],
- point_cloud_id: str | None = None,
Given a set of point clusters that may represent individual tree stems, circles and ellipses are fitted to multiple horinzontal layers of each cluster. To obtain the horizontal layers,
stem_search_circle_fitting_num_layershorizontal layers with a height ofstem_search_circle_fitting_layer_heightare created starting at a height ofstem_search_circle_fitting_layer_start. The layers have an overlap ofstem_search_circle_fitting_layer_overlapto the previous layer (the variables are constructor parameters).The points used for the circle and ellipse fitting in each layer are selected based on the results of a preliminary circle / ellipse fitting step. A buffer area is created around the outline of the respective circle or ellipse from the previous step. This buffer has a width of
stem_search_circle_fitting_small_buffer_widthif the preliminary circle or ellipse diameter is less than or equal tostem_search_circle_fitting_switch_buffer_thresholdand otherwisestem_search_circle_fitting_large_buffer_width(the variables are constructor parameters). All points from the respective layer that lie within the buffer area are selected and the fitting of the circles / ellipses is repeated using only these points.The ellipse fitting is only done if
stem_search_ellipse_fitting(constructor parameter) is set toTrue. In the ellipse fitting, ellipses are only kept if the ratio of the radius along the semi-minor axis to the radius along the semi-major axis is greater than or equal tostem_search_ellipse_filter_threshold(constructor parameter).- Parameters:
stem_layer_xyz (
ndarray[tuple[Any,...],dtype[float32|float64]]) – Point coordinates of the points within the stem layer as defined by the constructor parametersstem_search_min_zandstem_search_max_z.preliminary_layer_circles (
ndarray[tuple[Any,...],dtype[float32|float64]]) – Parameters of the preliminary circles. Each circle must be represented by three values, namely the x- and y-coordinates of its center and its radius. If the preliminary circle fitting was unsucessfull for the respective layer, all values must be set to-1.preliminary_layer_ellipses (
ndarray[tuple[Any,...],dtype[float32|float64]]) – Parameters of the preliminary ellipses. Each ellipse must represented by five values, namely the x- and y-coordinates of its center, its radius along the semi-major and along the semi-minor axis, and the counterclockwise angle of rotation from the x-axis to the semi-major axis of the ellipse. If the preliminary circle fitting was unsucessfull for the respective layer, all values must be set to-1.terrain_heights_at_cluster_positions (
ndarray[tuple[Any,...],dtype[float32|float64]]) – Terrain height at the centroid position of each cluster.point_cloud_id (
str|None) – ID of the point cloud to be used in the file names of the visualization results. If set toNone, no visualizations are created. (default:None)
- Returns:
- Tuple of four arrays:
Parameters of the circles that were fitted to the layers of each cluster. Each circle is represented by three values, namely the x- and y-coordinates of its center and its radius. If the circle fitting is not successful for a layer, all parameters are set to
-1.Parameters of the ellipses that were fitted to the layers of each cluster. Each ellipse is represented by five values, namely the x- and y-coordinates of its center, its radius along the semi-major and along the semi-minor axis, and the counterclockwise angle of rotation from the x-axis to the semi-major axis of the ellipse. If the ellipse fitting is not successful for a layer or results in an ellipse whose axis ratio is smaller than
stem_search_ellipse_filter_threshold(constructor parameter), all parameters are set to-1.X- and y-coordinates of the points in each horizontal layer of each cluster that were selected for the circle and ellipse fitting in that layer based on the preliminary circles or ellipses. Points belonging to the same layer of the same cluster are stored consecutively.
Number of points belonging to each horizontal layer of each cluster.
- Shape:
stem_layer_xyz: \((N, 3)\)preliminary_layer_circles: \((S, L, 3)\)preliminary_layer_ellipses: \((S, L, 5)\)terrain_heights: \((S)\)Output: \((S, L, 3)\), \((S, L, 5)\), \((L)\), \((N_{0,0} + ... + N_{S,L}, 2)\).
where\(N = \text{ number of points in the stem layer}\)\(S = \text{ number of stem clusters}\)\(L = \text{ number of horinzontal layers to which circles and ellipses are fitted}\)\(N_{s,l} = \text{ number of points selected from the l-th layer of cluster } s\)
- rename_visualizations_after_filtering(
- filter_mask: ndarray[tuple[Any, ...], dtype[bool]],
- point_cloud_id: str | None,
Renames visualization files that plot the circles / ellipses fitted to stem layers after the filtering of the stems. In the course of this, the stem IDs in the file names are updated and the postfix
_validis added to the file names for stem clusters that were kept during filtering, while the postfix_invalidis added to the file names for stem clusters that were filtered out.- Parameters:
filter_mask (
ndarray[tuple[Any,...],dtype[bool]]) – Boolean mask indicating which stems were kept during filtering.point_cloud_id (
str|None) – ID of the point cloud used in the file names of the visualizations. If set toNone, it is assumed that no visualizations were created.
- Shape:
filter_mask: \((S)\)
where\(S = \text{ number of stem clusters}\)
- segment_crowns(
- xyz: ndarray[tuple[Any, ...], dtype[float32 | float64]],
- dists_to_dtm: ndarray[tuple[Any, ...], dtype[float32 | float64]],
- is_tree: ndarray[tuple[Any, ...], dtype[bool]],
- stem_positions: ndarray[tuple[Any, ...], dtype[float32 | float64]],
- stem_diameters: ndarray[tuple[Any, ...], dtype[float32 | float64]],
- stem_labels: ndarray[tuple[Any, ...], dtype[int64]],
Computes a point-wise segmentation of the individual trees using a region growing procedure. In the first step, the region growing procedure selects an initial set of seed points for each tree. These should be points that are very likely to belong to the corresponding tree. In an iterative process, the sets of points assigned to each tree are then expanded. In each iteration, the neighboring points of each seed point within a certain search radius are determined. Neighboring points that are not yet assigned to any tree are added to the same tree as the seed point and become seed points in the next iteration. The region growing continues until there are no more seed points to be processed or the maximum number of iterations is reached.
To select the initial seed points for a given tree, the following approach is used: (1) All points that were assigned to the respective stem during the stem detection stage are used as seed points. (2) Additionally, a cylinder with a height of
tree_seg_seed_layer_height(constructor parameter) and a diameter oftree_seg_seed_diameter_factor * d(constructor parameter) is considered, wheredis the tree’s stem diameter at breast height, which has been computed in the previous step. The cylinder’s center is positioned at the stem center at breast height, which also has been computed in the previous stage. All points within the cylinder that have not yet been selected as seed points for other trees are selected as seed points.The search radius for the iterative region growing procedure is set as follows: First, the search radius is set to the voxel size used for voxel-based subsampling, which is done before starting the region growing procedure. The search radius is increased by the voxel size if one of the following conditions is fulfilled at the end of a region growing iteration:
The ratio between the number of points newly assigned to trees in the iteration and the number of remaining, unassigned points is below
tree_seg_min_total_assignment_ratio.The ratio between the number of trees to which new points have been assigned in the iteration and the total number of trees is below
tree_seg_min_tree_assignment_ratio.
The search radius is increased up to a maximum radius of
tree_seg_max_search_radius. If the search radius has not been increased fortree_seg_decrease_search_radius_after_num_iter, it is reduced by the voxel size (the variables are constructor parameters).To promote upward growth, the z-coordinates of the points are divided by
tree_seg_z_scalebefore the region growing.Since the terrain filtering in the first step of the algorithm may be inaccurate and some tree points may be falsely classified as terrain points, both terrain and non-terrain points are considered by the region growing procedure. However, to prevent large portions of terrain points from being included in tree instances, terrain points are only assigned to a tree if their cumulative search distance from the initial seed point is below the threshold defined by
tree_seg_cum_search_dist_include_ground(constructor parameter). The cumulative search distance is defined as the total distance traveled between consecutive points until reaching a terrain point.- Parameters:
xyz (
ndarray[tuple[Any,...],dtype[float32|float64]]) – Coordinates of the points which to consider in the region growing procedure. This can include both terrain and non-terrain points.dists_to_dtm (
ndarray[tuple[Any,...],dtype[float32|float64]]) – Height of each point above the ground.is_tree (
ndarray[tuple[Any,...],dtype[bool]]) – Boolean array indicating which points have been identified as potential tree points in the terrain classification step. The points for which the corresponding entry isTrueare considered in all region growing iterations, while terrain points are only considered if the cumulative search distance is below the threshold defined bytree_seg_cum_search_dist_include_ground(constructor parameter).stem_positions (
ndarray[tuple[Any,...],dtype[float32|float64]]) – X- and y-coordinates of the positions of the stems to be used for seed point selection.stem_diameters (
ndarray[tuple[Any,...],dtype[float32|float64]]) – Diameters of of the stems to be used for seed point selection.stem_labels (
ndarray[tuple[Any,...],dtype[int64]]) – Cluster labels for each point that represent the detected stems. Points that do not belong to any stem must have the label-1.
- Returns:
Tree instance labels for all points. For points not belonging to any tree, the label is set to
invalid_tree_id(constructor parameter).- Raises:
ValueError – If
xyz,dists_to_dtm, andis_treehave different lengths.ValueError – If
tree_positionsandstem_diametershave different lengths.
- Shape:
xyz: \((N, 3)\)dists_to_dtm: \((N)\)is_tree: \((N)\)tree_positions: \((S, 2)\)stem_diameters: \((S)\)cluster_labels: \((N)\)Output: \((N)\)
where\(N = \text{ number of points}\)\(S = \text{ number of detected stems}\)
- stem_diameter_estimation_gam(
- points: ndarray[tuple[Any, ...], dtype[float32 | float64]],
- center: ndarray[tuple[Any, ...], dtype[float32 | float64]],
Estimates the diameter of a tree stem at a certain height using a generalized additive model (GAM). It is assumed that a circle or an ellipse has already been fitted to the points of the tree stem in a layer around the respective height. To create the GAM, the points are converted into polar coordinates, using the center of the previously fitted circle or ellipse as the coordinate origin. The GAM is then fitted to predict the radius of the points based on the angles. The fitted GAM is then used to predict the stem radii in one-degree intervals. From these predictions, the stem’s boundary polygon is constructed and the stem diameter is computed from the area of the boundary polygon. Assuming the boundary polygon is approximately circular, the stem diameter is calculated using the formula for a circle’s diameter.
\[d = 2 \cdot \sqrt{\frac{A_{polygon}}{\pi}}\]If any of the predicted radii is negative or the difference between the minimum and maximum of the predicted radii is greater than the value of
stem_search_gam_max_radius_diff(constructor parameter), the fitted GAM is considered invalid, andNoneis returned for the stem diameter. In this case the diameter of the previously fitted circle or ellipse can be used as a more robust estimate of the stem diameter.- Parameters:
points (
ndarray[tuple[Any,...],dtype[float32|float64]]) – Points belonging to the stem layer for which to estimate the diameter.center (
ndarray[tuple[Any,...],dtype[float32|float64]]) – Center of the circle or ellipse that has been fitted to the stem layer.
- Returns:
- Tuple with two elements:
Estimated stem diameter. The estimated stem diameter may be
Noneif the fitted GAM is invalid.Array containing the sorted vertices of the stem’s boundary polygon predicted by the GAM as cartesian coordinates.
- Shape:
points: \((N, 2)\) or \((N, 3)\)center: \((2)\)
where\(N = \text{ number of points}\)
- class pointtree.instance_segmentation.TreeXPreset(
- invalid_tree_id: int = -1,
- num_workers: int | None = -1,
- visualization_folder: str | Path | None = None,
- random_seed: int = 0,
- csf_terrain_classification_threshold: float = 0.5,
- csf_tree_classification_threshold: float = 0.5,
- csf_correct_steep_slope: bool = False,
- csf_iterations: int = 500,
- csf_resolution: float = 0.5,
- csf_rigidness: int = 2,
- dtm_k: int = 400,
- dtm_power: float = 1,
- dtm_resolution: float = 0.25,
- dtm_voxel_size: float = 0.05,
- stem_search_min_z: float = 1.0,
- stem_search_max_z: float = 4.0,
- stem_search_voxel_size: float = 0.015,
- stem_search_dbscan_2d_eps: float = 0.025,
- stem_search_dbscan_2d_min_points: int = 90,
- stem_search_dbscan_3d_eps: float = 0.1,
- stem_search_dbscan_3d_min_points: int = 15,
- stem_search_min_cluster_points: int | None = 300,
- stem_search_min_cluster_height: float | None = 1.5,
- stem_search_min_cluster_intensity: float | None = 6000,
- stem_search_pc1_min_explained_variance: float | None = None,
- stem_search_max_inclination: float | None = None,
- stem_search_refined_circle_fitting: bool = False,
- stem_search_ellipse_fitting: bool = False,
- stem_search_circle_fitting_method: Literal['m-estimator', 'ransac'] = 'ransac',
- stem_search_circle_fitting_layer_start: float = 1.0,
- stem_search_circle_fitting_num_layers: int = 15,
- stem_search_circle_fitting_layer_height: float = 0.225,
- stem_search_circle_fitting_layer_overlap: float = 0.025,
- stem_search_circle_fitting_bandwidth: float = 0.01,
- stem_search_circle_fitting_min_points: int = 15,
- stem_search_circle_fitting_min_fitting_score: float = 100.0,
- stem_search_circle_fitting_min_stem_diameter: float = 0.02,
- stem_search_circle_fitting_max_stem_diameter: float = 1.0,
- stem_search_circle_fitting_min_completeness_idx: float | None = 0.3,
- stem_search_circle_fitting_small_buffer_width: float = 0.06,
- stem_search_circle_fitting_large_buffer_width: float = 0.09,
- stem_search_circle_fitting_switch_buffer_threshold: float = 0.3,
- stem_search_ellipse_filter_threshold: float = 0.6,
- stem_search_circle_fitting_max_std_diameter: float = 0.04,
- stem_search_circle_fitting_max_std_position: float | None = None,
- stem_search_circle_fitting_std_num_layers: int = 6,
- stem_search_gam_buffer_width: float = 0.03,
- stem_search_gam_max_radius_diff: float | None = 0.3,
- tree_seg_voxel_size: float = 0.05,
- tree_seg_z_scale: float = 2,
- tree_seg_seed_layer_height: float = 0.6,
- tree_seg_seed_diameter_factor: float = 1.05,
- tree_seg_seed_min_diameter: float = 0.05,
- tree_seg_min_total_assignment_ratio: float = 0.002,
- tree_seg_min_tree_assignment_ratio: float = 0.3,
- tree_seg_max_search_radius: float = 0.5,
- tree_seg_decrease_search_radius_after_num_iter: int = 10,
- tree_seg_max_iterations: int = 500,
- tree_seg_cum_search_dist_include_terrain: float = 0.8,
Bases:
TreeXPresetDefault,MappingPreset for the treeX algorithm containing the default settings of the algorithm.
- class pointtree.instance_segmentation.TreeXPresetOriginal(
- invalid_tree_id: int = -1,
- num_workers: int | None = -1,
- visualization_folder: str | Path | None = None,
- random_seed: int = 0,
- csf_terrain_classification_threshold: float = 0.5,
- csf_tree_classification_threshold: float = 0.5,
- csf_correct_steep_slope: bool = False,
- csf_iterations: int = 500,
- csf_resolution: float = 0.5,
- csf_rigidness: int = 2,
- dtm_k: int = 400,
- dtm_power: float = 1,
- dtm_resolution: float = 0.25,
- dtm_voxel_size: float = 0.05,
- stem_search_min_z: float = 1.0,
- stem_search_max_z: float = 3.0,
- stem_search_voxel_size: float = 0.015,
- stem_search_dbscan_2d_eps: float = 0.025,
- stem_search_dbscan_2d_min_points: int = 90,
- stem_search_dbscan_3d_eps: float = 0.1,
- stem_search_dbscan_3d_min_points: int = 15,
- stem_search_min_cluster_points: int | None = 300,
- stem_search_min_cluster_height: float | None = 1.3,
- stem_search_min_cluster_intensity: float | None = 6000,
- stem_search_pc1_min_explained_variance: float | None = None,
- stem_search_max_inclination: float | None = None,
- stem_search_refined_circle_fitting: bool = True,
- stem_search_ellipse_fitting: bool = True,
- stem_search_circle_fitting_method: Literal['m-estimator', 'ransac'] = 'ransac',
- stem_search_circle_fitting_layer_start: float = 1.0,
- stem_search_circle_fitting_num_layers: int = 14,
- stem_search_circle_fitting_layer_height: float = 0.125,
- stem_search_circle_fitting_layer_overlap: float = 0.025,
- stem_search_circle_fitting_bandwidth: float = 0.01,
- stem_search_circle_fitting_min_points: int = 50,
- stem_search_circle_fitting_min_fitting_score: float = 100.0,
- stem_search_circle_fitting_min_stem_diameter: float = 0.02,
- stem_search_circle_fitting_max_stem_diameter: float = 1.0,
- stem_search_circle_fitting_min_completeness_idx: float | None = 0.3,
- stem_search_circle_fitting_small_buffer_width: float = 0.06,
- stem_search_circle_fitting_large_buffer_width: float = 0.09,
- stem_search_circle_fitting_switch_buffer_threshold: float = 0.3,
- stem_search_ellipse_filter_threshold: float = 0.6,
- stem_search_circle_fitting_max_std_diameter: float = 0.0185,
- stem_search_circle_fitting_max_std_position: float | None = None,
- stem_search_circle_fitting_std_num_layers: int = 6,
- stem_search_gam_buffer_width: float = 0.03,
- stem_search_gam_max_radius_diff: float | None = None,
- tree_seg_voxel_size: float = 0.05,
- tree_seg_z_scale: float = 2,
- tree_seg_seed_layer_height: float = 0.6,
- tree_seg_seed_diameter_factor: float = 1.05,
- tree_seg_seed_min_diameter: float = 0.05,
- tree_seg_min_total_assignment_ratio: float = 0.002,
- tree_seg_min_tree_assignment_ratio: float = 0.3,
- tree_seg_max_search_radius: float = 0.5,
- tree_seg_decrease_search_radius_after_num_iter: int = 10,
- tree_seg_max_iterations: int = 500,
- tree_seg_cum_search_dist_include_terrain: float = 0.8,
Bases:
TreeXPresetPreset for the treeX algorithm with settings similar to those used in the papers Tockner, Andreas, et al. “Automatic Tree Crown Segmentation Using Dense Forest Point Clouds from Personal Laser Scanning (PLS).” International Journal of Applied Earth Observation and Geoinformation 114 (2022): 103025.. and
- class pointtree.instance_segmentation.TreeXPresetTLS(
- invalid_tree_id: int = -1,
- num_workers: int | None = -1,
- visualization_folder: str | Path | None = None,
- random_seed: int = 0,
- csf_terrain_classification_threshold: float = 0.5,
- csf_tree_classification_threshold: float = 0.5,
- csf_correct_steep_slope: bool = False,
- csf_iterations: int = 500,
- csf_resolution: float = 0.5,
- csf_rigidness: int = 2,
- dtm_k: int = 400,
- dtm_power: float = 1,
- dtm_resolution: float = 0.25,
- dtm_voxel_size: float = 0.05,
- stem_search_min_z: float = 1.0,
- stem_search_max_z: float = 4.0,
- stem_search_voxel_size: float = 0.015,
- stem_search_dbscan_2d_eps: float = 0.025,
- stem_search_dbscan_2d_min_points: int = 90,
- stem_search_dbscan_3d_eps: float = 0.1,
- stem_search_dbscan_3d_min_points: int = 15,
- stem_search_min_cluster_points: int | None = 300,
- stem_search_min_cluster_height: float | None = 1.5,
- stem_search_min_cluster_intensity: float | None = 6000,
- stem_search_pc1_min_explained_variance: float | None = None,
- stem_search_max_inclination: float | None = None,
- stem_search_refined_circle_fitting: bool = False,
- stem_search_ellipse_fitting: bool = False,
- stem_search_circle_fitting_method: Literal['m-estimator', 'ransac'] = 'ransac',
- stem_search_circle_fitting_layer_start: float = 1.0,
- stem_search_circle_fitting_num_layers: int = 15,
- stem_search_circle_fitting_layer_height: float = 0.225,
- stem_search_circle_fitting_layer_overlap: float = 0.025,
- stem_search_circle_fitting_bandwidth: float = 0.01,
- stem_search_circle_fitting_min_points: int = 15,
- stem_search_circle_fitting_min_fitting_score: float = 100.0,
- stem_search_circle_fitting_min_stem_diameter: float = 0.02,
- stem_search_circle_fitting_max_stem_diameter: float = 1.0,
- stem_search_circle_fitting_min_completeness_idx: float | None = 0.3,
- stem_search_circle_fitting_small_buffer_width: float = 0.06,
- stem_search_circle_fitting_large_buffer_width: float = 0.09,
- stem_search_circle_fitting_switch_buffer_threshold: float = 0.3,
- stem_search_ellipse_filter_threshold: float = 0.6,
- stem_search_circle_fitting_max_std_diameter: float = 0.04,
- stem_search_circle_fitting_max_std_position: float | None = None,
- stem_search_circle_fitting_std_num_layers: int = 6,
- stem_search_gam_buffer_width: float = 0.03,
- stem_search_gam_max_radius_diff: float | None = 0.3,
- tree_seg_voxel_size: float = 0.05,
- tree_seg_z_scale: float = 2,
- tree_seg_seed_layer_height: float = 0.6,
- tree_seg_seed_diameter_factor: float = 1.05,
- tree_seg_seed_min_diameter: float = 0.05,
- tree_seg_min_total_assignment_ratio: float = 0.002,
- tree_seg_min_tree_assignment_ratio: float = 0.3,
- tree_seg_max_search_radius: float = 0.5,
- tree_seg_decrease_search_radius_after_num_iter: int = 10,
- tree_seg_max_iterations: int = 500,
- tree_seg_cum_search_dist_include_terrain: float = 0.8,
Bases:
TreeXPresetPreset for the treeX algorithm for dense terrestrial point clouds (e.g., from stationary or mobile scanning).
- class pointtree.instance_segmentation.TreeXPresetULS(
- invalid_tree_id: int = -1,
- num_workers: int | None = -1,
- visualization_folder: str | Path | None = None,
- random_seed: int = 0,
- csf_terrain_classification_threshold: float = 0.5,
- csf_tree_classification_threshold: float = 0.5,
- csf_correct_steep_slope: bool = False,
- csf_iterations: int = 500,
- csf_resolution: float = 0.5,
- csf_rigidness: int = 2,
- dtm_k: int = 400,
- dtm_power: float = 1,
- dtm_resolution: float = 0.25,
- dtm_voxel_size: float = 0.05,
- stem_search_min_z: float = 1.0,
- stem_search_max_z: float = 5.0,
- stem_search_voxel_size: float = 0.015,
- stem_search_dbscan_2d_eps: float = 0.07,
- stem_search_dbscan_2d_min_points: int = 15,
- stem_search_dbscan_3d_eps: float = 0.3,
- stem_search_dbscan_3d_min_points: int = 1,
- stem_search_min_cluster_points: int | None = 20,
- stem_search_min_cluster_height: float = 1.5,
- stem_search_min_cluster_intensity: float | None = 6000,
- stem_search_pc1_min_explained_variance: float | None = None,
- stem_search_max_inclination: float | None = None,
- stem_search_refined_circle_fitting: bool = False,
- stem_search_ellipse_fitting: bool = False,
- stem_search_circle_fitting_method: Literal['m-estimator', 'ransac'] = 'ransac',
- stem_search_circle_fitting_layer_start: float = 1.0,
- stem_search_circle_fitting_num_layers: int = 4,
- stem_search_circle_fitting_layer_height: float = 1.4,
- stem_search_circle_fitting_layer_overlap: float = 0.4,
- stem_search_circle_fitting_bandwidth: float = 0.03,
- stem_search_circle_fitting_min_points: int = 15,
- stem_search_circle_fitting_min_fitting_score: float = 5.0,
- stem_search_circle_fitting_min_stem_diameter: float = 0.02,
- stem_search_circle_fitting_max_stem_diameter: float = 1.0,
- stem_search_circle_fitting_min_completeness_idx: float | None = 0.3,
- stem_search_circle_fitting_small_buffer_width: float = 0.06,
- stem_search_circle_fitting_large_buffer_width: float = 0.09,
- stem_search_circle_fitting_switch_buffer_threshold: float = 0.3,
- stem_search_ellipse_filter_threshold: float = 0.6,
- stem_search_circle_fitting_max_std_diameter: float = 0.1,
- stem_search_circle_fitting_max_std_position: float | None = None,
- stem_search_circle_fitting_std_num_layers: int = 2,
- stem_search_gam_buffer_width: float = 0.03,
- stem_search_gam_max_radius_diff: float | None = 0.3,
- tree_seg_voxel_size: float = 0.05,
- tree_seg_z_scale: float = 2,
- tree_seg_seed_layer_height: float = 0.6,
- tree_seg_seed_diameter_factor: float = 1.05,
- tree_seg_seed_min_diameter: float = 0.05,
- tree_seg_min_total_assignment_ratio: float = 0.002,
- tree_seg_min_tree_assignment_ratio: float = 0.3,
- tree_seg_max_search_radius: float = 0.5,
- tree_seg_decrease_search_radius_after_num_iter: int = 10,
- tree_seg_max_iterations: int = 500,
- tree_seg_cum_search_dist_include_terrain: float = 0.8,
Bases:
TreeXPresetPreset for the treeX algorithm for sparser UAV-borne point clouds.
pointtree.instance_segmentation.filters
Methods to filter instances.
- pointtree.instance_segmentation.filters.filter_instances_intensity(
- intensities: ndarray[tuple[Any, ...], dtype[float32 | float64]],
- instance_ids: ndarray[tuple[Any, ...], dtype[int64]],
- unique_instance_ids: ndarray[tuple[Any, ...], dtype[int64]],
- min_intensity: float | None,
- threshold_percentile: float = 0.8,
- inplace: bool = False,
Removes instances, for which the specified percentile of the reflection intensities of the instance points is below than or equal to
min_intensity.- Parameters:
instance_ids (
ndarray[tuple[Any,...],dtype[int64]]) – Instance IDs of the points.unique_instance_ids (
ndarray[tuple[Any,...],dtype[int64]]) – Unique instance IDs.min_intensity (
float|None) – Maximum intensity at which instances are discarded. If set toNone, the instances are not filtered.threshold_percentile (
float) – Percentile of the reflection intensity values to be used for the filtering. The percentile must be specified as a value in \([0, 1]\). (default:0.8)inplace (
bool) – Whether the filtering should be applied inplace to theinstance_idsarray. (default:False)
- Returns:
- Tuple of two arrays:
Updated instance ID of each point. Points that do not belong to any instance are assigned the ID
-1.Unique instance IDs.
- Shape:
instance_ids: \((N)\)unique_instance_ids: \((I)\)Output: \((N)\), \((I')\)
where\(N = \text{ number of points}\)\(I = \text{ number of instances before filtering}\)\(I' = \text{ number of instances after filtering}\)
- pointtree.instance_segmentation.filters.filter_instances_min_points(
- instance_ids: ndarray[tuple[Any, ...], dtype[int64]],
- unique_instance_ids: ndarray[tuple[Any, ...], dtype[int64]],
- min_points: int | None,
- inplace: bool = False,
Removes instances with less than
min_points.- Parameters:
instance_ids (
ndarray[tuple[Any,...],dtype[int64]]) – Instance IDs of the points.unique_instance_ids (
ndarray[tuple[Any,...],dtype[int64]]) – Unique instance IDs.min_points (
int|None) – Minimum number of points an instance must have to not be discarded. If set toNone, the instances are not filtered.inplace (
bool) – Whether the filtering should be applied inplace to theinstance_idsarray. (default:False)
- Returns:
- Tuple of two arrays:
Updated instance ID of each point. Points that do not belong to any instance are assigned the ID
-1.Unique instance IDs.
- Shape:
instance_ids: \((N)\)unique_instance_ids: \((I)\)Output: \((N)\), \((I')\)
where\(N = \text{ number of points}\)\(I = \text{ number of instances before filtering}\)\(I' = \text{ number of instances after filtering}\)
- pointtree.instance_segmentation.filters.filter_instances_pca(
- xyz: ndarray[tuple[Any, ...], dtype[float32 | float64]],
- instance_ids: ndarray[tuple[Any, ...], dtype[int64]],
- unique_instance_ids: ndarray[tuple[Any, ...], dtype[int64]],
- min_explained_variance: float | None,
- max_inclination: float | None,
- inplace: bool = False,
Performs a principal component analysis on the points of each instance and removes instances for which the first principal component explains less than
min_explained_varianceof the point’s variance or the inclination angle between the z-axis and the first principal component is greater thanmax_inclination.- Parameters:
instance_ids (
ndarray[tuple[Any,...],dtype[int64]]) – Instance IDs of the points.unique_instance_ids (
ndarray[tuple[Any,...],dtype[int64]]) – Unique instance IDs.min_explained_variance (
float|None) – Minimum percentage of variance that the first principal component of an instance must explain in order to not be discarded.max_inclination (
float|None) – Maximum inclination angle to the z-axis that the first principal component of an instance can have before being discarded (in degree).inplace (
bool) – Whether the filtering should be applied inplace to theinstance_idsarray. (default:False)
- Returns:
- Tuple of two arrays:
Updated instance ID of each point. Points that do not belong to any instance are assigned the ID
-1.Unique instance IDs.
- Shape:
xyz: \((N, 3)\)instance_ids: \((N)\)unique_instance_ids: \((I)\)Output: \((N)\), \((I')\)
where\(N = \text{ number of points}\)\(I = \text{ number of instances before filtering}\)\(I' = \text{ number of instances after filtering}\)
- pointtree.instance_segmentation.filters.filter_instances_vertical_extent(
- xyz: ndarray[tuple[Any, ...], dtype[float32 | float64]],
- instance_ids: ndarray[tuple[Any, ...], dtype[int64]],
- unique_instance_ids: ndarray[tuple[Any, ...], dtype[int64]],
- min_vertical_extent: float | None,
- inplace: bool = False,
Removes instances whose extent in z-direction is less than
min_vertical_extent.- Parameters:
instance_ids (
ndarray[tuple[Any,...],dtype[int64]]) – Instance IDs of the points.unique_instance_ids (
ndarray[tuple[Any,...],dtype[int64]]) – Unique instance IDs.min_vertical_extent (
float|None) – Minimum vertical extent an instance must have to not be discarded. If set toNone, the instances are not filtered.inplace (
bool) – Whether the filtering should be applied inplace to theinstance_idsarray. (default:False)
- Returns:
- Tuple of two arrays:
Updated instance ID of each point. Points that do not belong to any instance are assigned the ID
-1.Unique instance IDs.
- Shape:
xyz: \((N, 3)\)instance_ids: \((N)\)unique_instance_ids: \((I)\)Output: \((N)\), \((I')\)
where\(N = \text{ number of points}\)\(I = \text{ number of instances before filtering}\)\(I' = \text{ number of instances after filtering}\)