sylva.als.tiles¶
Catalog, Chunk, catalog, apply, retile and write_tiles are the
objects of sylva.als, documented there.
sylva.als.tiles ¶
Point clouds of any size as tiles, with results identical to the whole cloud.
A plot scanned from a hundred positions holds hundreds of millions of points,
more than fit in memory with room to process them. This module keeps such a
plot as square LAS/LAZ tiles and processes it one tile at a time, each with a
buffer of points from its neighbours, on the engine of sylva.als
(Catalog, Chunk, apply, retile and
write_tiles are the same objects here as there):
from_scansbuilds the tiles from the scans, one scan at a time, thinning them on one global voxel grid and recording each point'sscan_id. The tiles hold exactly whatsylva.filters.voxel_downsamplekeeps of all the scans concatenated, on the same grid.voxel_downsample,statistical_outlier_removal,radius_outlier_removal,estimate_normals,planarity_linearityanddetect_stemsgive, tile by tile, the result of their single-cloud counterparts on the whole plot. The neighbourhood operations are exact for any buffer: a point whose neighbours might lie beyond it is searched again in the tiles within its reach, one tile at a time.classify_ground,dtmandnormalizeare thesylva.alsoperations with settings for plots.
Tiles keep every attribute they carry (tree_id, wood, scan_id,
height, ...): LAS dimensions by name, others as extra bytes. Each
function that writes tiles returns the Catalog of what it wrote;
last_run tells how many points were held at once.
Examples:
>>> from sylva.als import tiles
>>> cat = tiles.from_scans(sorted(Path("scans").glob("*.laz")), "tiles/",
... tile_size=10.0, voxel_size=0.02, transforms=sops)
>>> clean = tiles.statistical_outlier_removal(cat, "sor/", k=6, std_ratio=1.0)
>>> tiles.last_run().max_points
RunInfo
dataclass
¶
What a tiled run did, to check that memory stayed bounded.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
chunks
|
int
|
Tiles processed (written, for |
required |
max_points
|
int
|
Most points held at once by one tile: the tile with its buffer, a
tile read again for points reaching beyond it, the graph nodes of a
tile and its buffer ( |
required |
points_read
|
int
|
Points read over the run, buffers and second reads included. |
required |
rereads
|
int
|
Tiles searched a second time because some of their points had
neighbours that could lie beyond the buffer (for
|
required |
widened_points
|
int
|
Points evaluated from such a second search (graph nodes of the wider
runs, for |
required |
TreeStore ¶
Each tree's points of a segmented plot, one directory per tree.
Written by split_trees. A tree's points are those of every tile
it touches, in catalogue order (the order the whole plot has them in),
at full precision and with all their attributes. Per-point values of a
tree (leaf / wood, say) can be kept beside it (set_values) and
written back to the tiles (classify_leaf_wood does so).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str | Path
|
The store's directory. |
required |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
bounds ¶
bounds(tree_id: int) -> tuple[float, ...]
(xmin, ymin, zmin, xmax, ymax, zmax) of one tree's points.
tiles_of ¶
tiles_of(tree_id: int) -> list[str]
Names of the tiles holding points of one tree, in catalogue order.
read ¶
read(tree_id: int) -> PointCloud
One tree's points.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tree_id
|
int
|
Which tree. |
required |
Returns:
| Type | Description |
|---|---|
PointCloud
|
In the order the whole plot has them, with every attribute. |
values ¶
values(tree_id: int, name: str) -> np.ndarray | None
Values kept for one tree under name (None if there are none).
has_values ¶
has_values(tree_id: int, name: str) -> bool
Whether values are kept for one tree under name.
set_values ¶
set_values(tree_id: int, name: str, values) -> None
Keep a value per point of one tree (in read order) under name.
Raises:
| Type | Description |
|---|---|
ValueError
|
For a count other than the tree's points, or a name that is not a plain word. |
map ¶
map(fn, ids=None, workers: int | None = None, label: str = 'trees') -> dict
fn(tree_id, cloud) for every tree, a few trees at a time.
Trees are taken largest first, each read when a worker is free, so
at most workers trees are in memory; workers is lowered when
that many of the largest tree would not fit in the memory budget
(sylva.util.limits, BYTES_PER_TREE_POINT per point). The
work runs on threads: Sylva's computations release the interpreter,
so threads run them in parallel.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fn
|
Called with a tree's id and points; its result is kept. |
required | |
ids
|
Trees to do; all if None. |
None
|
|
workers
|
int | None
|
Trees at once; the number of CPUs if None. |
None
|
label
|
str
|
For the progress bar. |
'trees'
|
Returns:
| Type | Description |
|---|---|
dict
|
|
PlotRun
dataclass
¶
What run_plot made, and where.
Attributes:
| Name | Type | Description |
|---|---|---|
out |
Path
|
The run's directory. |
trees |
list[Tree]
|
The trees, as |
table |
list[dict]
|
One row per tree: the tree, its crown metrics and its QSM's volume. |
qsms |
PlotQSMs
|
The models. |
catalogs |
dict[str, Catalog]
|
|
dtm |
Raster
|
The terrain model. |
store |
TreeStore
|
The per-tree store. |
timings |
dict[str, float]
|
Seconds per stage (0 for a stage taken from an earlier run). |
memory |
dict[str, int]
|
Most points held at once per stage ( |
last_run ¶
last_run() -> RunInfo | None
The RunInfo of the last tiled operation of this module.
Returns:
| Type | Description |
|---|---|
RunInfo or None
|
None before any has run, and after |
from_scans ¶
from_scans(scans, out: str | Path, tile_size: float = 10.0, voxel_size: float | None = None, transforms=None, origin=(0.0, 0.0, 0.0), bounds=None, format: str = 'laz', point_format: int = 6, scale: float = 0.001, epsg: int | None = None, workers: int | None = None, tolerance: float = 1.0) -> Catalog
Build square tiles from scans, thinned on one global voxel grid.
The scans are taken one at a time, in order. Each is moved by its
transform, cropped to bounds, and keeps the first point of each
voxel it has; those points go, tile by tile, to scratch files in
out at full precision. Each tile is then assembled keeping the first
point per voxel in scan order, and written with a scan_id attribute
(the scan's position in scans). Tiles are cut along voxel
boundaries, so no voxel is split between two tiles.
The tiles hold exactly the points that
filters.voxel_downsample(concatenated, voxel_size, origin=origin)
keeps of all the (transformed, cropped) scans concatenated in order,
with their attributes; only their coordinates are rounded to scale.
Only one scan, and one tile per worker, is in memory at a time.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
scans
|
Paths of point cloud files (any format |
required | |
out
|
str | Path
|
Directory for the tiles, named |
required |
tile_size
|
float
|
Side of the tiles (m); a whole number of voxels. |
10.0
|
voxel_size
|
float | None
|
Voxel size (m) of the thinning; None keeps every point. |
None
|
transforms
|
One 4x4 matrix (or None) per scan, applied before anything else
(a scan's SOP, say), as |
None
|
|
origin
|
|
(0.0, 0.0, 0.0)
|
|
bounds
|
|
None
|
|
format
|
str
|
|
'laz'
|
point_format
|
int
|
LAS point data record format of the tiles. |
6
|
scale
|
float
|
Coordinate quantisation of the tiles (m). |
0.001
|
epsg
|
int | None
|
EPSG code recorded in each tile. |
None
|
workers
|
int | None
|
Tiles assembled at once; the number of CPUs if None, fewer if memory is short. |
None
|
tolerance
|
float
|
For the returned catalogue (see |
1.0
|
Returns:
| Type | Description |
|---|---|
Catalog
|
The tiles, south to north and west to east. |
Raises:
| Type | Description |
|---|---|
ValueError
|
For a tile size that is not a whole number of voxels, bad bounds, a transform that is not 4x4, or non-finite coordinates. |
OSError
|
For a scan that cannot be read. |
Notes
The scratch files hold every scan's thinned points (about 40 bytes each plus the attributes) until the tiles are written; they are removed afterwards, and after an error.
voxel_downsample ¶
voxel_downsample(catalog: Catalog, out: str | Path, voxel_size: float, origin=(0.0, 0.0, 0.0), workers: int | None = None, format: str | None = None) -> Catalog
Thin tiles to the first point of each voxel of one global grid.
Points are taken in catalogue order (tile by tile, each in file order),
so the result is filters.voxel_downsample(cat.read(), voxel_size,
origin=origin): a voxel that straddles two tiles keeps one point, from
the first tile that has one. Each tile is read with a buffer of one
voxel, which is all this needs.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
catalog
|
Catalog
|
The tiles (a |
required |
out
|
str | Path
|
Directory for the thinned tiles, one per input tile with its name. |
required |
voxel_size
|
float
|
Voxel size (m). |
required |
origin
|
|
(0.0, 0.0, 0.0)
|
|
workers
|
int | None
|
Tiles processed at once; the number of CPUs if None, fewer if memory is short. The result does not depend on it. |
None
|
format
|
str | None
|
|
None
|
Returns:
| Type | Description |
|---|---|
Catalog
|
The written tiles. |
Raises:
| Type | Description |
|---|---|
ValueError
|
For a non-positive voxel size or an output that would overwrite an input tile. |
statistical_outlier_removal ¶
statistical_outlier_removal(catalog: Catalog, out: str | Path, k: int = 8, std_ratio: float = 2.0, classify: bool = False, buffer: float = 1.0, workers: int | None = None, format: str | None = None) -> Catalog
Statistical outlier removal over all the tiles, as on the whole cloud.
CloudCompare's SOR (sylva.filters.statistical_outlier_removal)
compares each point's mean distance to its k nearest neighbours with
mean + std_ratio * std of that distance over the whole cloud. Tiled,
it takes two passes: the per-point distances tile by tile (each with its
buffer, and a wider second read for any point whose neighbours could lie
beyond it), kept in scratch files beside the output; then the mean and
standard deviation over every point, summed in catalogue order as the
whole-cloud filter sums them; then the tiles are written with the
threshold applied. The points kept are exactly those
filters.statistical_outlier_removal(cat.read(), k, std_ratio) keeps.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
catalog
|
Catalog
|
The tiles. |
required |
out
|
str | Path
|
Directory for the filtered tiles. |
required |
k
|
int
|
Neighbours per point. |
8
|
std_ratio
|
float
|
Threshold in standard deviations; lower removes more. |
2.0
|
classify
|
bool
|
Keep every point and set |
False
|
buffer
|
float
|
Width (m) of the band of neighbouring points read around each tile.
The result does not depend on it; wider than the |
1.0
|
workers
|
int | None
|
Tiles processed at once; see |
None
|
format
|
str | None
|
|
None
|
Returns:
| Type | Description |
|---|---|
Catalog
|
The written tiles. |
Raises:
| Type | Description |
|---|---|
ValueError
|
For |
radius_outlier_removal ¶
radius_outlier_removal(catalog: Catalog, out: str | Path, radius: float, min_neighbors: int = 4, classify: bool = False, buffer: float | None = None, workers: int | None = None, format: str | None = None) -> Catalog
Radius outlier removal over all the tiles, as on the whole cloud.
Points with fewer than min_neighbors others within radius are
removed, exactly as sylva.filters.radius_outlier_removal removes
them from the whole cloud.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
catalog
|
Catalog
|
The tiles. |
required |
out
|
str | Path
|
Directory for the filtered tiles. |
required |
radius
|
float
|
Search radius (m). |
required |
min_neighbors
|
int
|
Points with fewer other points within |
4
|
classify
|
bool
|
Keep every point and set |
False
|
buffer
|
float | None
|
Band read around each tile (m); 1 cm more than |
None
|
workers
|
int | None
|
Tiles processed at once; see |
None
|
format
|
str | None
|
|
None
|
Returns:
| Type | Description |
|---|---|
Catalog
|
The written tiles. |
Raises:
| Type | Description |
|---|---|
ValueError
|
For a non-positive radius or a negative buffer. |
estimate_normals ¶
estimate_normals(catalog: Catalog, out: str | Path, k: int = 12, buffer: float = 1.0, workers: int | None = None, format: str | None = None) -> Catalog
Add surface normals to every point of the tiles.
Each point gets normal_x, normal_y and normal_z: the values
sylva.filters.estimate_normals gives it in the whole cloud (the
eigenvector of the smallest eigenvalue of its k neighbours'
covariance; the sign is arbitrary).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
catalog
|
Catalog
|
The tiles. |
required |
out
|
str | Path
|
Directory for the tiles with normals. |
required |
k
|
int
|
Neighbours per point (at least 3 are used). |
12
|
buffer
|
float
|
Band read around each tile (m); the result does not depend on it. |
1.0
|
workers
|
int | None
|
Tiles processed at once; see |
None
|
format
|
str | None
|
|
None
|
Returns:
| Type | Description |
|---|---|
Catalog
|
The written tiles. |
Raises:
| Type | Description |
|---|---|
ValueError
|
For |
Notes
Neighbours at equal distances are ordered by the search tree, whose
layout differs between a tile and the whole cloud; where such a tie
falls at the k-th neighbour (common with quantised coordinates), a
point's value can come from another neighbour. On the Tumbarumba plot
this affected 0.02 per cent of the points.
planarity_linearity ¶
planarity_linearity(catalog: Catalog, out: str | Path, k: int = 20, buffer: float = 1.0, workers: int | None = None, format: str | None = None) -> Catalog
Add planarity and linearity to every point of the tiles.
Each point gets planarity and linearity, the values
sylva.filters.planarity_linearity gives it in the whole cloud.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
catalog
|
Catalog
|
The tiles. |
required |
out
|
str | Path
|
Directory for the tiles with the features. |
required |
k
|
int
|
Neighbours per point (at least 3 are used). |
20
|
buffer
|
float
|
Band read around each tile (m); the result does not depend on it. |
1.0
|
workers
|
int | None
|
Tiles processed at once; see |
None
|
format
|
str | None
|
|
None
|
Returns:
| Type | Description |
|---|---|
Catalog
|
The written tiles. |
Raises:
| Type | Description |
|---|---|
ValueError
|
For |
Notes
Neighbours at equal distances are ordered by the search tree, whose
layout differs between a tile and the whole cloud; where such a tie
falls at the k-th neighbour (common with quantised coordinates), a
point's value can come from another neighbour. On the Tumbarumba plot
this affected 0.02 per cent of the points.
classify_ground ¶
classify_ground(catalog: Catalog, out: str | Path, method: str = 'csf', cloth_resolution: float = 0.5, rigidness: int = 2, class_threshold: float = 0.3, iterations: int = 500, time_step: float = 0.65, cell_size: float = 0.5, max_window: float = 10.0, slope: float = 0.3, initial_distance: float = 0.15, max_distance: float = 2.0, buffer: float = 10.0, workers: int | None = None, format: str | None = None) -> Catalog
Classify ground in every tile (sylva.als.classify_ground, one chunk per tile).
Each tile's own points are written with classification 2 (ground) or
1 (other); points classified 7 or 18 (noise, e.g. by
statistical_outlier_removal with classify) take no part and
keep their class.
With method="pmf" the result is that of
sylva.ground.classify_ground_pmf on the whole cloud once the
buffer covers how far the filter looks: its grid is anchored at
multiples of cell_size in every tile, and each opening of half-width
w cells reaches 2 w cells, so buffer >= 2 * cell_size * (1 + 2
+ 4 + ... + W) (W the largest half-width, max_window /
cell_size or less) plus the widest gap in the ground is sufficient;
narrower buffers agree everywhere but near the tile edges in practice.
method="csf" is not exact: the cloth is one sheet whose settling
depends on the whole of it, so points near a tile edge can differ from
the whole-cloud classification (rarely, with a buffer of a few metres).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
catalog
|
Catalog
|
The tiles. |
required |
out
|
str | Path
|
Directory for the classified tiles. |
required |
method
|
('csf', 'pmf')
|
Cloth simulation or progressive morphological filter. |
"csf"
|
cloth_resolution
|
float
|
CSF parameters ( |
0.5
|
rigidness
|
float
|
CSF parameters ( |
0.5
|
class_threshold
|
float
|
CSF parameters ( |
0.5
|
iterations
|
float
|
CSF parameters ( |
0.5
|
time_step
|
float
|
CSF parameters ( |
0.5
|
cell_size
|
float
|
PMF parameters ( |
0.5
|
max_window
|
float
|
PMF parameters ( |
0.5
|
slope
|
float
|
PMF parameters ( |
0.5
|
initial_distance
|
float
|
PMF parameters ( |
0.5
|
max_distance
|
float
|
PMF parameters ( |
0.5
|
buffer
|
float
|
Band read around each tile (m). |
10.0
|
workers
|
int | None
|
Tiles processed at once; see |
None
|
format
|
str | None
|
|
None
|
Returns:
| Type | Description |
|---|---|
Catalog
|
The written tiles. |
Raises:
| Type | Description |
|---|---|
ValueError
|
For an unknown method or bad parameters. |
dtm ¶
dtm(catalog: Catalog, resolution: float = 0.5, method: str = 'lowest', buffer: float = 10.0, workers: int | None = None) -> Raster
Digital terrain model of all the tiles (sylva.als.dtm, one chunk per tile).
Away from the outer edge of the plot, and with a buffer wider than the
widest gap in the ground, each cell is that of
sylva.ground.make_dtm on the ground points of the whole cloud.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
catalog
|
Catalog
|
Tiles with ground classified ( |
required |
resolution
|
float
|
Cell size (m). |
0.5
|
method
|
('lowest', 'tin', 'natural', 'idw')
|
As in |
"lowest"
|
buffer
|
float
|
Band read around each tile (m). |
10.0
|
workers
|
int | None
|
Tiles processed at once; see |
None
|
Returns:
| Type | Description |
|---|---|
Raster
|
On a grid anchored at multiples of |
Raises:
| Type | Description |
|---|---|
ValueError
|
For an unknown method, or tiles without ground points. |
normalize ¶
normalize(catalog: Catalog, out: str | Path, dtm='auto', dtm_resolution: float = 0.5, replace_z: bool = False, buffer: float = 10.0, workers: int | None = None, format: str | None = None) -> Catalog
Height above ground for every point of the tiles (sylva.als.normalize).
With dtm="auto" each tile makes its DTM from its own and its
buffer's ground points at dtm_resolution, as dtm would, so
the heights are those of sylva.ground.normalize_height with the
whole-cloud sylva.ground.make_dtm (to rounding of the cell
positions, below a nanometre).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
catalog
|
Catalog
|
Tiles with ground classified. |
required |
out
|
str | Path
|
Directory for the normalised tiles. |
required |
dtm
|
'auto' or Raster
|
Make the DTM per tile, or use this one (from |
'auto'
|
dtm_resolution
|
float
|
Cell size (m) of the |
0.5
|
replace_z
|
bool
|
Replace z by the height (keeping the elevation in |
False
|
buffer
|
float
|
Band read around each tile (m); wider than the widest gap in the
ground for |
10.0
|
workers
|
int | None
|
Tiles processed at once; see |
None
|
format
|
str | None
|
|
None
|
Returns:
| Type | Description |
|---|---|
Catalog
|
The written tiles. |
Raises:
| Type | Description |
|---|---|
ValueError
|
For a bad |
detect_stems ¶
detect_stems(catalog: Catalog, height_attr: str = 'height', buffer: float = 2.0, workers: int | None = None, **params) -> list[Tree]
Detect stems over all the tiles, as in the whole cloud.
sylva.trees.detect_stems runs on each tile with its buffer, and
each tile keeps the stems whose position (at the reference height) lies
in it, or nearest to it. RANSAC draws its samples from a random stream
per cluster (cluster_seeds=True), so a stem depends only on the
points around it: with a buffer wider than the clusters and circles of
the stems at a tile edge (max_cluster_extent, 2 m, is enough), the
stems are those trees.detect_stems(cat.read(), cluster_seeds=True,
**params) finds, with the same ids.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
catalog
|
Catalog
|
Height-normalised tiles ( |
required |
height_attr
|
str
|
Attribute holding heights; z is used if the tiles do not have it. |
'height'
|
buffer
|
float
|
Band read around each tile (m). |
2.0
|
workers
|
int | None
|
Tiles processed at once; see |
None
|
**params
|
Keyword parameters of |
{}
|
Returns:
| Type | Description |
|---|---|
list of Tree
|
Sorted by |
Raises:
| Type | Description |
|---|---|
ValueError
|
For an unknown parameter or a negative buffer. |
segment_trees ¶
segment_trees(catalog: Catalog, stems: list[Tree], out: str | Path, height_attr: str = 'height', buffer: float = 20.0, max_buffer: float = 60.0, merge=True, percentile: float = 100.0, prune=True, attribute: str = 'tree_id', voxel_origin=(0.0, 0.0, 0.0), edge_margin: float = 2.0, workers: int | None = None, format: str | None = None, **params) -> tuple[list[Tree], Catalog]
Segment the trees of height-normalised tiles, as on the whole plot.
The whole-plot sequence is sylva.trees.merge_branches,
sylva.trees.segment_trees, sylva.trees.tree_heights and
sylva.trees.prune_trees. Here each tree is segmented by the tile
whose core holds its stem, from the graph nodes of that tile and of a
buffer around it, and every point gets the label of its nearest node;
the tiles are written to out with each point's tree id in
attribute (-1 for none). Tree ids are those the whole-plot sequence
gives, unique over the plot.
The graph is built on the points above cut_above_ground thinned to
one per voxel of a grid with a corner at voxel_origin, found tile by
tile exactly as in the whole cloud. With a buffer wider than the largest
crown (every tree that competes with a tile's trees for points then has
its stem, seeds and crown within the buffer), the labels, heights and
trees are those of the whole-plot sequence with the same
voxel_origin, up to ties in the nearest-neighbour searches. A tile
whose trees reach the edge of its buffer (a node within edge_margin
of it, where the plot goes on) is read again with a buffer twice as wide,
up to max_buffer; trees still at the edge are named in a warning and
flagged crown_at_edge in their extra.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
catalog
|
Catalog
|
Height-normalised tiles ( |
required |
stems
|
list[Tree]
|
Stems from |
required |
out
|
str | Path
|
Directory for the segmented tiles. |
required |
height_attr
|
str
|
Attribute holding heights; z is used if the tiles do not have it. |
'height'
|
buffer
|
float
|
Band of neighbouring nodes read around each tile (m); wider than the largest crown. |
20.0
|
max_buffer
|
float
|
Widest band a tile is read again with (m). |
60.0
|
merge
|
Merge branches into their stems first ( |
True
|
|
percentile
|
float
|
Height percentile of |
100.0
|
prune
|
Prune afterwards ( |
True
|
|
attribute
|
str
|
Name of the label attribute written to the tiles (int32). |
'tree_id'
|
voxel_origin
|
Corner of the voxel grid of the graphs. |
(0.0, 0.0, 0.0)
|
|
edge_margin
|
float
|
How close to the edge of the buffer (m) a tree's nodes may come before its tile is read again. |
2.0
|
workers
|
int | None
|
Tiles processed at once; the number of CPUs if None, fewer if memory is short. The result does not depend on it. |
None
|
format
|
str | None
|
|
None
|
**params
|
Keywords of |
{}
|
Returns:
| Name | Type | Description |
|---|---|---|
trees |
list of Tree
|
The trees as |
catalog |
Catalog
|
The segmented tiles. |
Raises:
| Type | Description |
|---|---|
ValueError
|
For bad parameters, stems without unique ids, or a plot that does not fit in memory one tile at a time. |
Warns:
| Type | Description |
|---|---|
UserWarning
|
When crowns reach the edge of the widest buffer, or nodes were claimed by two tiles. |
split_trees ¶
split_trees(catalog: Catalog, out: str | Path, attribute: str = 'tree_id', workers: int | None = None) -> TreeStore
Write every tree's points to a TreeStore, one tile at a time.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
catalog
|
Catalog
|
Segmented tiles ( |
required |
out
|
str | Path
|
Directory of the store; trees already in it are replaced. |
required |
attribute
|
str
|
Attribute holding each point's tree id; below 0 is no tree. |
'tree_id'
|
workers
|
int | None
|
Tiles at once; see |
None
|
Returns:
| Type | Description |
|---|---|
TreeStore
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
For tiles without |
read_tree ¶
read_tree(source, tree_id: int, attribute: str = 'tree_id', bounds=None) -> PointCloud
One tree's points, without loading the plot.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
A |
required | |
tree_id
|
int
|
Which tree. |
required |
attribute
|
str
|
Label attribute of the tiles. |
'tree_id'
|
bounds
|
|
None
|
Returns:
| Type | Description |
|---|---|
PointCloud
|
The tree's points in catalogue order, with their attributes. |
classify_leaf_wood ¶
classify_leaf_wood(store, catalog: Catalog, out: str | Path, min_points: int = 100, voxel_size: float = 0.02, method: str = 'gbs', attribute: str = 'tree_id', name: str = 'wood', resume: bool = False, workers: int | None = None, format: str | None = None, **wood_params) -> Catalog
Leaf / wood of every tree, written to the tiles as wood.
Each tree of min_points points or more is classified on its own
points (sylva.leaves.classify_leaf_wood, which is defined on one
tree), a few trees at a time; the tiles are then written to out with
name (int8): 1 wood, 0 leaf, -1 for points of no tree or of a tree
too small. The values are those of classifying plot[labels ==
tree_id] of the whole plot.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
store
|
The trees ( |
required | |
catalog
|
Catalog
|
The segmented tiles the store was split from. |
required |
out
|
str | Path
|
Directory for the tiles with |
required |
min_points
|
int
|
Smaller trees are left at -1. |
100
|
voxel_size
|
float
|
As in |
0.02
|
method
|
float
|
As in |
0.02
|
**wood_params
|
float
|
As in |
0.02
|
attribute
|
str
|
Label attribute of the tiles. |
'tree_id'
|
name
|
str
|
Attribute written, and the name the values are kept under in the store. |
'wood'
|
resume
|
bool
|
Keep values already in the store rather than classifying again. |
False
|
workers
|
int | None
|
Trees, then tiles, at once. |
None
|
format
|
str | None
|
|
None
|
Returns:
| Type | Description |
|---|---|
Catalog
|
The written tiles. |
build_qsms ¶
build_qsms(store, stems=None, voxel_size: float = 0.01, wood: bool = True, buttress: bool = False, min_points: int = 2000, height_attr: str = 'height', resume: bool = False, ids=None, workers: int | None = None, **params) -> qsm.PlotQSMs
A QSM for every tree of a store, a few trees at a time.
Each tree is modelled from its own points as sylva.qsm.build_plot
models it in the whole plot (thinning, the wood filter, the stem centre
and DBH anchor from stems, a buttress if asked for), so the models
are those of build_plot(plot, labels, stems, ...). Trees run in
parallel, each one's fit parallel inside; at most workers trees are
in memory.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
store
|
The trees ( |
required | |
stems
|
The trees of |
None
|
|
voxel_size
|
float
|
As in |
0.01
|
wood
|
float
|
As in |
0.01
|
buttress
|
float
|
As in |
0.01
|
min_points
|
float
|
As in |
0.01
|
height_attr
|
float
|
As in |
0.01
|
resume
|
bool
|
Keep models already saved in the store ( |
False
|
ids
|
Trees to model; every tree of the store if None. |
None
|
|
workers
|
int | None
|
Trees at once; the number of CPUs if None, fewer if memory is short. |
None
|
**params
|
Passed to |
{}
|
Returns:
| Type | Description |
|---|---|
PlotQSMs
|
As |
crown_metrics ¶
crown_metrics(store, height_attr: str = 'height', crown_base_fraction: float = 0.1, workers: int | None = None) -> dict[int, dict]
sylva.trees.crown_metrics for every tree of a store.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
store
|
The trees ( |
required | |
height_attr
|
str
|
Attribute holding heights. |
'height'
|
crown_base_fraction
|
float
|
Density threshold for the crown base. |
0.1
|
workers
|
int | None
|
Trees at once. |
None
|
Returns:
| Type | Description |
|---|---|
dict
|
|
run_plot ¶
run_plot(scans, out: str | Path, transforms=None, use=None, bounds=None, plot=None, tile_size: float = 10.0, voxel_size: float = 0.02, read_options: dict | None = None, sor: dict | None = None, ground: dict | None = None, dtm_resolution: float = 0.5, ground_voxel: float = 0.05, stems: dict | None = None, merge=None, segment: dict | None = None, percentile: float = 99.0, prune=None, buffer: float = 20.0, max_buffer: float = 60.0, leaf_wood: dict | None = None, qsm_options: dict | None = None, qsm_files: bool = False, scale: float = 0.001, workers: int | None = None, log=print) -> PlotRun
The plot workflow from registered scans to trees and QSMs, in tiles.
Stages, each saved in its own directory of out and skipped when a
rerun finds it complete (so an interrupted run resumes; per-tree stages
resume tree by tree):
====================== ==============================================================
scans/ (RiSCAN projects only) each scan read, moved by its SOP, cropped to bounds and thinned
tiles/ sylva.als.tiles.from_scans: tiles thinned to voxel_size on one grid
sor/ sylva.als.tiles.statistical_outlier_removal
ground/ sylva.als.tiles.classify_ground on a ground_voxel thinning; the DTM in dtm.npz (full precision, used for the heights) and dtm.asc
heights/ sylva.als.tiles.normalize with that DTM
stems.pkl sylva.als.tiles.detect_stems on the tiles inside plot
segmented/ segment_trees, with trees.pkl
trees/ split_trees, with per-tree leaf / wood and QSMs
wood/ classify_leaf_wood
qsm_table.csv build_qsms, with qsm_models.pkl
trees.csv the trees with their crown metrics and QSM volumes
====================== ==============================================================
Memory is bounded by the tile size, the buffers and workers, not by
the plot: no stage holds more than a few tiles with their buffers, or a
few trees.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
scans
|
Registered scan files or point clouds in scan order (as
|
required | |
out
|
str | Path
|
Directory of the run. |
required |
transforms
|
One 4x4 matrix (or None) per scan, applied first: the registration of the files, or for a project the corrections applied after each SOP. |
None
|
|
use
|
One flag per scan; scans flagged False are left out. |
None
|
|
bounds
|
|
None
|
|
plot
|
|
None
|
|
tile_size
|
float
|
Of |
10.0
|
voxel_size
|
float
|
Of |
10.0
|
scale
|
float
|
Of |
10.0
|
read_options
|
dict | None
|
For a project: options of |
None
|
sor
|
dict | None
|
Keywords of |
None
|
ground
|
dict | None
|
Keywords of |
None
|
dtm_resolution
|
float
|
Cell size of the DTM (m). |
0.5
|
ground_voxel
|
float
|
Thinning of the points ground is classified on (m). |
0.05
|
stems
|
dict | None
|
Keywords of |
None
|
merge
|
As in |
None
|
|
prune
|
As in |
None
|
|
segment
|
dict | None
|
Keywords of |
None
|
percentile
|
float
|
Height percentile of the trees. |
99.0
|
buffer
|
float
|
Of |
20.0
|
max_buffer
|
float
|
Of |
20.0
|
leaf_wood
|
dict | None
|
Keywords of |
None
|
qsm_options
|
dict | None
|
Keywords of |
None
|
qsm_files
|
bool
|
Also write each model's cylinders and mesh to |
False
|
workers
|
int | None
|
Tiles or trees at once. |
None
|
log
|
Called with a line of text per stage; None for silence. |
print
|
Returns:
| Type | Description |
|---|---|
PlotRun
|
|