Skip to content

Commit

Permalink
added skeleton path length example to docs (#945)
Browse files Browse the repository at this point in the history
* added skeleton path length example to docs

* Update docs/src/webknossos-py/examples/load_annotation_from_file.md

Co-authored-by: Norman Rzepka <[email protected]>

* Update docs/src/webknossos-py/examples/load_annotation_from_file.md

Co-authored-by: Norman Rzepka <[email protected]>

* added tests

* fix test for load_annotation_file example

* fix test

* fix tests?

---------

Co-authored-by: Norman Rzepka <[email protected]>
  • Loading branch information
hotzenklotz and normanrz authored Sep 21, 2023
1 parent 9601235 commit eee7de9
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 0 deletions.
2 changes: 2 additions & 0 deletions docs/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
11 changes: 11 additions & 0 deletions docs/src/webknossos-py/examples/load_annotation_from_file.md
Original file line number Diff line number Diff line change
@@ -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<--
```
9 changes: 9 additions & 0 deletions docs/src/webknossos-py/examples/skeleton_path_length.md
Original file line number Diff line number Diff line change
@@ -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<--
```
37 changes: 37 additions & 0 deletions webknossos/examples/load_annotation_from_file.py
Original file line number Diff line number Diff line change
@@ -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)
55 changes: 55 additions & 0 deletions webknossos/examples/skeleton_path_length.py
Original file line number Diff line number Diff line change
@@ -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)
Binary file not shown.
22 changes: 22 additions & 0 deletions webknossos/tests/test_examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

0 comments on commit eee7de9

Please sign in to comment.