diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 20a06f78a..a821e9075 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -96,6 +96,8 @@ nav: - webknossos-py/examples/skeleton_synapse_candidates.md - webknossos-py/examples/calculate_segment_sizes.md - webknossos-py/examples/download_segments.md + - webknossos-py/examples/load_annotation_from_file.md + - webknossos-py/examples/skeleton_path_length.md - Administration Examples: - webknossos-py/examples/user_times.md - webknossos-py/examples/annotation_project_administration.md diff --git a/docs/src/webknossos-py/examples/load_annotation_from_file.md b/docs/src/webknossos-py/examples/load_annotation_from_file.md new file mode 100644 index 000000000..e28c4cb39 --- /dev/null +++ b/docs/src/webknossos-py/examples/load_annotation_from_file.md @@ -0,0 +1,11 @@ +# Annotation File to OME-TIFF + +This example shows how to turn a volume annotation downloaded from WEBKNOSSOS into a OME-TIFF. When [manually downloading a WEBKNOSSOS annotation](/webknossos/export.html#data-export-through-the-ui) through the UI you end up with a ZIP file containing the volume segmentation in the WKW-format (and potentially any skeleton annotation). + +As an alternative to manually downloading annotation files, have a look at streaming the data directly from the remote serve, e.g., in [this example](./download_segments.html). + +```python +--8<-- +webknossos/examples/load_annotation_from_file.py +--8<-- +``` diff --git a/docs/src/webknossos-py/examples/skeleton_path_length.md b/docs/src/webknossos-py/examples/skeleton_path_length.md new file mode 100644 index 000000000..314ec27e4 --- /dev/null +++ b/docs/src/webknossos-py/examples/skeleton_path_length.md @@ -0,0 +1,9 @@ +# Skeleton Path Length + +This example shows how to downloaded a skeleton annotation from WEBKNOSSOS and calcuate the total path length along all edges of each tree. + +```python +--8<-- +webknossos/examples/skeleton_path_length.py +--8<-- +``` diff --git a/webknossos/examples/load_annotation_from_file.py b/webknossos/examples/load_annotation_from_file.py new file mode 100644 index 000000000..b0b6783f4 --- /dev/null +++ b/webknossos/examples/load_annotation_from_file.py @@ -0,0 +1,37 @@ +from pathlib import Path + +from tifffile import imwrite + +import webknossos as wk + +# Specify a bounding box for cutouts +# (topleft_x, topleft_y, topleft_z), (width, height, depth) +BOUNDING_BOX = wk.BoundingBox((0, 0, 0), (500, 500, 50)) + + +def load_annotation(annotation_file: Path) -> None: + # Read the WEBKNOSSOS annotation file (a zipped WKW) + annotation = wk.Annotation.load(annotation_file) + + # Treat it as a regular WK volume layer + with annotation.temporary_volume_layer_copy() as segmentation_layer: + # Do any standard layer operation, e.g. reading a cutout as a numpy array + mag_view = segmentation_layer.get_finest_mag() + segments = mag_view.read(absolute_bounding_box=BOUNDING_BOX) + + # Write segmentation IDs to an OME Tiff file + imwrite( + "segmentation.ome.tiff", + segments.T, # note, the tiff lib use different channel order + ome=True, + metadata={ + "axes": "ZYXC", + }, + ) + + +if __name__ == "__main__": + # Path to annotation file on your computer + ANNOTATION_FILE = Path("my_annotation_file.zip") + + load_annotation(ANNOTATION_FILE) diff --git a/webknossos/examples/skeleton_path_length.py b/webknossos/examples/skeleton_path_length.py new file mode 100644 index 000000000..3ce356247 --- /dev/null +++ b/webknossos/examples/skeleton_path_length.py @@ -0,0 +1,55 @@ +from typing import Tuple + +import numpy as np + +import webknossos as wk + + +def calculate_path_length(annotation_url: str, auth_token: str) -> None: + with wk.webknossos_context(token=auth_token): + # Download a annotation directly from the WEBKNOSSOS server + annotation = wk.Annotation.download( + annotation_url, + ) + + skeleton = annotation.skeleton + voxel_size = annotation.voxel_size + + # Iterate over all the tree in a skeleton and calculate their path length + for tree in skeleton.flattened_trees(): + path_length = calculate_path_length_for_tree(tree, voxel_size) + + # Log the results :-) + print(f"Tree {tree.name} has a path length of {path_length:.2f} nm") + + +def calculate_path_length_for_tree( + tree: wk.Tree, voxel_size: Tuple[float, float, float] +) -> float: + # Auxillary method calculate the maximum path length of a given tree + # Assumes that the annotation does not contain any cycles + + assert ( + len(tree.nodes) > 1 + ), "Each tree should have at least two nodes to calculate the path length" + result = 0 + + # Iterate over all edges + for source_node, target_node in tree.edges: + diff_vector = np.array(source_node.position) - np.array(target_node.position) + scaled_diff_vector = diff_vector * voxel_size + edge_length = np.sqrt(scaled_diff_vector.dot(scaled_diff_vector)) + result += edge_length + + return result + + +if __name__ == "__main__": + # Authentication and API token for your account + # Get it at https://webknossos.org/auth/token + TOKEN = "YOUR-token" + + # A WEBKNOSOS URL containing the skeleton annotation + ANNOTATION_URL = "https://webknossos.org/annotations/12345678" + + calculate_path_length(ANNOTATION_URL, TOKEN) diff --git a/webknossos/tests/example_files/l4dense_motta_et_al_demo_v2__explorational.zip b/webknossos/tests/example_files/l4dense_motta_et_al_demo_v2__explorational.zip new file mode 100644 index 000000000..1f82a5f28 Binary files /dev/null and b/webknossos/tests/example_files/l4dense_motta_et_al_demo_v2__explorational.zip differ diff --git a/webknossos/tests/test_examples.py b/webknossos/tests/test_examples.py index 860dfe750..07e3ae487 100644 --- a/webknossos/tests/test_examples.py +++ b/webknossos/tests/test_examples.py @@ -326,3 +326,25 @@ def test_download_tiff_stack() -> None: len(list(output_path.iterdir())) == mag_view.bounding_box.size.z / mag_view.mag.z ) + + +@pytest.mark.block_network(allowed_hosts=[".*"]) +@pytest.mark.vcr(ignore_hosts=["webknossos.org", "data-humerus.webknossos.org"]) +def test_skeleton_path_length() -> None: + from examples.skeleton_path_length import calculate_path_length + + # Public skeleton annotation by MH Lab + annotation_id = "https://webknossos.org/annotations/62b191ef010000e80033e7c0" + token = "123" + calculate_path_length(annotation_id, token) + + +def test_load_annotation_file() -> None: + from examples.load_annotation_from_file import load_annotation + + annotation_file = Path( + "./tests/example_files/l4dense_motta_et_al_demo_v2__explorational.zip" + ).resolve() + + with tmp_cwd(): + load_annotation(annotation_file)