Habitat patterns¶
Structural classes of habitat pixels (MSPA, SPA), objects by size, parcellation and boundary contortion. Tutorial: habitat patterns.
MSPA¶
mspa
¶
Morphological Spatial Pattern Analysis (MSPA).
Soille, P. & Vogt, P. (2009). Morphological segmentation of binary patterns. Pattern Recognition Letters 30(4), 456-459. doi:10.1016/j.patrec.2008.10.015
mspa
¶
mspa(
data,
*,
connectivity: int = 8,
edge_width: int = 1,
transition: bool = True,
internal: bool = True,
n_threads: int | None = None,
validate: bool = True,
output_dir=None,
n_workers: int | None = None,
max_memory: int | str | None = None,
overwrite: bool = False,
)
Morphological Spatial Pattern Analysis, identical to GuidosToolbox's MSPA.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
Map coded |
required | |
connectivity
|
int
|
Foreground connectivity, 8 (default) or 4 (GTB "FGconn"). |
8
|
edge_width
|
int
|
Effective edge width in pixels, 1-255 (GTB "EdgeWidth"). |
1
|
transition
|
bool
|
Whether loops/bridges crossing edges or perforations are shown as
transitions (GTB "Transition"). As in GuidosToolbox this only changes
the colour table written by |
True
|
internal
|
bool
|
Distinguish internal features with a +100 offset and label core-openings (100) and border-openings (220) (GTB "Intext"). |
True
|
n_threads
|
int | None
|
Threads used inside one map; |
None
|
validate
|
bool
|
Check the input like GuidosToolbox before processing. |
True
|
output_dir
|
Collections only. With |
None
|
|
n_workers
|
Collections only. With |
None
|
|
max_memory
|
Collections only. With |
None
|
|
overwrite
|
Collections only. With |
None
|
Returns:
| Type | Description |
|---|---|
Same container type as ``data`` with the ``uint8`` MSPA classes; see
|
|
`MSPA_CLASSES`. DataArrays keep coordinates and georeferencing and
|
|
record the parameters in ``attrs``. For a collection, a list (see
|
|
``output_dir``).
|
|
See Also
mspa_stats : area of every class. mspa_tiled : maps larger than memory. spa : simplified pattern analysis (2 to 6 classes).
Examples:
>>> import landschaft as ls
>>> forest = ls.load_raster(ls.example_data("input"))
>>> morph = ls.mspa(forest, edge_width=2)
>>> morph.shape, str(morph.dtype)
((1000, 1000), 'uint8')
>>> ls.MSPA_CLASSES[17], ls.MSPA_CLASSES[33]
('Core', 'Bridge')
>>> _ = ls.write_raster(morph, "forest_mspa.tif") # with the MSPA colours
4-connected habitat and without internal features:
mspa_stats
¶
Area of every MSPA class.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
result
|
An MSPA map returned by |
required |
Returns:
| Type | Description |
|---|---|
DataFrame
|
Indexed by class (Core, Islet, Perforation, Edge, Loop, Bridge, Branch,
Background, Core-Opening, Border-Opening, Missing), with |
Examples:
mspa_stats
¶
Area of every MSPA class.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
result
|
An MSPA map returned by |
required |
Returns:
| Type | Description |
|---|---|
DataFrame
|
Indexed by class (Core, Islet, Perforation, Edge, Loop, Bridge, Branch,
Background, Core-Opening, Border-Opening, Missing), with |
Examples:
MSPA class codes¶
ls.MSPA_CLASSES maps each code of an MSPA map to its class:
| Code | Class | Code | Class |
|---|---|---|---|
| 17 | Core | 65 | Loop |
| 9 | Islet | 67 | Loop in edge |
| 5 | Perforation | 69 | Loop in perforation |
| 3 | Edge | 33 | Bridge |
| 1 | Branch | 35 | Bridge in edge |
| 0 | Background | 37 | Bridge in perforation |
| 100 | Core-opening | 220 | Border-opening |
| 129 | Missing | +100 | the class inside a core-opening (internal=True) |
SPA¶
spa
¶
Simplified Pattern Analysis (GuidosToolbox Pattern -> Morphological -> SPA2/3/5/6).
Simplified alternatives to MSPA for the 8-connected foreground, with a fixed edge width of one pixel:
- SPA2: contiguous foreground and small linear features (SLF: foreground removed by a 3x3 opening);
- SPA3: core, core-opening and margin;
- SPA5: core, edge, perforation, margin (and core-opening);
- SPA6: SPA5 plus islets (foreground objects without core).
The maps and statistics are identical to GTB's.
SPAResult
dataclass
¶
SPAResult(
map: DataArray | ndarray,
variant: int,
stats: DataFrame,
counts: dict[str, int],
areas: dict[str, int],
porosity: float,
)
Bases: Saveable
Result of spa().
Attributes:
| Name | Type | Description |
|---|---|---|
map |
DataArray | ndarray
|
|
stats |
DataFrame
|
Foreground classes with area [pixels] and their share of the data area and of the foreground (GTB's SPA report). |
counts |
dict[str, int]
|
Number of 8-connected objects per class (core-openings: 4-connected) and of foreground objects, as GTB reports them. |
porosity |
float
|
|
Examples:
>>> import landschaft as ls
>>> result = ls.spa(ls.load_raster(ls.example_data("input")), variant=6)
>>> result.map.shape
(1000, 1000)
spa
¶
spa(data, variant: int = 5, *, n_threads: int = 0) -> SPAResult
Simplified pattern analysis: the habitat in 2, 3, 5 or 6 classes.
A coarser, faster summary than mspa():
2: contiguous habitat (core, edge, perforation) and small linear features (margin);3: core, edge (with perforation) and margin;5: core, edge, perforation and margin;6: core, edge, perforation, margin and islet.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
2-D |
required | |
variant
|
int
|
2, 3, 5 (default) or 6. |
5
|
n_threads
|
int
|
Threads ( |
0
|
Returns:
| Type | Description |
|---|---|
SPAResult
|
The class map, class areas, object counts and porosity. |
Examples:
SPAResult
dataclass
¶
SPAResult(
map: DataArray | ndarray,
variant: int,
stats: DataFrame,
counts: dict[str, int],
areas: dict[str, int],
porosity: float,
)
Bases: Saveable
Result of spa().
Attributes:
| Name | Type | Description |
|---|---|---|
map |
DataArray | ndarray
|
|
stats |
DataFrame
|
Foreground classes with area [pixels] and their share of the data area and of the foreground (GTB's SPA report). |
counts |
dict[str, int]
|
Number of 8-connected objects per class (core-openings: 4-connected) and of foreground objects, as GTB reports them. |
porosity |
float
|
|
Examples:
>>> import landschaft as ls
>>> result = ls.spa(ls.load_raster(ls.example_data("input")), variant=6)
>>> result.map.shape
(1000, 1000)
Objects by size¶
accounting
¶
Accounting: foreground objects grouped into size classes.
Reimplements GuidosToolbox's Image Analysis → Objects → Accounting (and its
batch version): every foreground object is labelled, measured and assigned
to one of up to six size classes. The maps and numbers are identical to the
GTB outputs <name>_acc.tif, <name>_acc_ids.tif,
<name>_acc_pixels.tif and <name>_acc.txt/.csv.
Reference
Vogt, P. & Riitters, K. (2017). GuidosToolbox: universal digital image object analysis. European Journal of Remote Sensing, 50(1), 352-361. doi:10.1080/22797254.2017.1330650
AccountingResult
dataclass
¶
AccountingResult(
classes: DataArray | ndarray,
ids: DataArray | ndarray,
area: DataArray | ndarray,
stats: DataFrame,
object_area: ndarray,
largest: tuple[tuple[int, ...], ...],
thresholds: tuple[int, ...],
connectivity: int,
pixel_resolution: float,
highlighted: bool,
)
Bases: Saveable
Result of accounting().
Attributes:
| Name | Type | Description |
|---|---|---|
classes |
DataArray | ndarray
|
|
ids |
DataArray | ndarray
|
|
area |
DataArray | ndarray
|
|
stats |
DataFrame
|
One row per size class: range, colour, number of objects, area and their percentages of all objects / of the total foreground area. |
object_area |
ndarray
|
Area of object |
largest |
tuple[tuple[int, ...], ...]
|
Ids of the largest, second and third largest objects (objects of equal area share a rank). |
highlighted |
bool
|
Whether the three largest objects are painted pink in |
Examples:
>>> import landschaft as ls
>>> result = ls.accounting(ls.load_raster(ls.example_data("input")))
>>> result.largest[0]
(1,)
>>> int(result.object_area[0])
214811
accounting
¶
accounting(
data: DataArray | ndarray | str | Path,
thresholds: Sequence[int] | str = DEFAULT_THRESHOLDS,
*,
connectivity: int = 8,
highlight_largest: bool = False,
pixel_resolution: float = 25.0,
n_threads: int = 0,
) -> AccountingResult
Group the foreground objects into size classes (GTB Accounting).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
DataArray | ndarray | str | Path
|
2-D |
required |
thresholds
|
Sequence[int] | str
|
Upper bounds [pixels] of the size classes, 1 to 5 non-decreasing
values >= 1 (repeats are dropped, as in GTB). Class 1 holds objects of
up to |
DEFAULT_THRESHOLDS
|
connectivity
|
int
|
Foreground connectivity, 8 (default) or 4. |
8
|
highlight_largest
|
bool
|
Paint the three largest objects pink (code 80), GTB's "big3pink". Only applied when there are at least three objects, as in GTB. |
False
|
pixel_resolution
|
float
|
Pixel size in metres, used for the hectare/acre factors of the report. |
25.0
|
n_threads
|
int
|
OpenMP threads (0: all cores but one). |
0
|
Returns:
| Type | Description |
|---|---|
AccountingResult
|
|
Examples:
>>> import landschaft as ls
>>> forest = ls.load_raster(ls.example_data("input"))
>>> result = ls.accounting(forest, thresholds=(50, 250, 1000, 5000, 20000))
>>> result.n_objects
2850
>>> result.stats[["size_class", "range", "objects"]].head(2)
size_class range objects
0 1 1 - 50 2668
1 2 51 - 250 129
>>> _ = ls.write_raster(result.classes, "size_classes.tif")
Classes proposed from the range of object sizes:
AccountingResult
dataclass
¶
AccountingResult(
classes: DataArray | ndarray,
ids: DataArray | ndarray,
area: DataArray | ndarray,
stats: DataFrame,
object_area: ndarray,
largest: tuple[tuple[int, ...], ...],
thresholds: tuple[int, ...],
connectivity: int,
pixel_resolution: float,
highlighted: bool,
)
Bases: Saveable
Result of accounting().
Attributes:
| Name | Type | Description |
|---|---|---|
classes |
DataArray | ndarray
|
|
ids |
DataArray | ndarray
|
|
area |
DataArray | ndarray
|
|
stats |
DataFrame
|
One row per size class: range, colour, number of objects, area and their percentages of all objects / of the total foreground area. |
object_area |
ndarray
|
Area of object |
largest |
tuple[tuple[int, ...], ...]
|
Ids of the largest, second and third largest objects (objects of equal area share a rank). |
highlighted |
bool
|
Whether the three largest objects are painted pink in |
Examples:
>>> import landschaft as ls
>>> result = ls.accounting(ls.load_raster(ls.example_data("input")))
>>> result.largest[0]
(1,)
>>> int(result.object_area[0])
214811
Parcels and shape¶
parcellation
¶
parcellation(
data: DataArray | ndarray | str | Path,
connectivity: int = 8,
return_labels: bool = False,
*,
n_threads: int | None = None,
) -> ParcellationResult
How finely the landscape is divided into parcels.
Every connected region of one value (e.g. habitat and background, or the classes of a land-cover map) is a parcel. Per class and for the whole map: the number of parcels, the average parcel size (APS), the area-weighted average parcel size (AWAPS, the size of the parcel a random pixel falls in), the division index (probability that two random pixels are in different parcels) and the degree of parcellation (PARC).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
DataArray | ndarray | str | Path
|
Class raster (array, DataArray or path; integer classes, 0 = NoData). |
required |
connectivity
|
int
|
Pixels of one parcel touch by a side (4) or also by a corner (8). |
8
|
return_labels
|
bool
|
Also return a map of the parcels. |
False
|
n_threads
|
int | None
|
Threads ( |
None
|
Returns:
| Type | Description |
|---|---|
ParcellationResult
|
|
Examples:
>>> import landschaft as ls
>>> forest = ls.load_raster(ls.example_data("input"))
>>> result = ls.parcellation(forest)
>>> list(result.stats.columns)
['Class', 'Count', 'Area[pixels]', 'APS', 'AWAPS', 'AWAPS/data', 'DIVISION', 'PARC[%]']
>>> result.global_stats["total_parcels"]
4020
>>> labels = ls.parcellation(forest, return_labels=True).parcel_labels
ParcellationResult
dataclass
¶
ParcellationResult(
stats: DataFrame,
global_stats: dict[str, Any],
parcel_labels: DataArray | ndarray | None = None,
)
Bases: Saveable
Results from landscape parcellation analysis.
Attributes:
| Name | Type | Description |
|---|---|---|
stats |
DataFrame
|
Class-level parcellation metrics (Count, Area, APS, AWAPS, DIVISION, PARC). |
global_stats |
dict[str, Any]
|
Landscape-level metrics across all valid classes. |
parcel_labels |
(DataArray or ndarray, optional)
|
Labeled parcels raster (if return_labels=True was requested). |
Examples:
>>> import landschaft as ls
>>> result = ls.parcellation(ls.load_raster(ls.example_data("input")))
>>> round(result.global_stats["division"], 3)
0.689
contortion
¶
contortion(
data: DataArray | ndarray | str | Path,
min_area: int = 1,
return_display: bool = False,
) -> DataArray | ndarray
How convoluted the boundary of every habitat object is.
For each habitat object, a measure of how much its boundary bends relative to its size: compact objects score low, elongated or dendritic ones high.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
DataArray | ndarray | str | Path
|
Habitat map (array, DataArray or path) coded 0 missing, 1 background, 2 habitat. |
required |
min_area
|
int
|
Objects smaller than this (pixels), or without an interior (no 3 x 3 core), are left out and coded 2. |
1
|
return_display
|
bool
|
Rescale the values to 3-255 for display (the byte scale of the colour
table |
False
|
Returns:
| Type | Description |
|---|---|
DataArray or ndarray
|
0 missing, 1 background, 2 left-out objects, 3 and above the contortion of each object. |
Examples: