Skip to content

Commit eee7de9

Browse files
added skeleton path length example to docs (#945)
* 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]>
1 parent 9601235 commit eee7de9

File tree

7 files changed

+136
-0
lines changed

7 files changed

+136
-0
lines changed

docs/mkdocs.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ nav:
9696
- webknossos-py/examples/skeleton_synapse_candidates.md
9797
- webknossos-py/examples/calculate_segment_sizes.md
9898
- webknossos-py/examples/download_segments.md
99+
- webknossos-py/examples/load_annotation_from_file.md
100+
- webknossos-py/examples/skeleton_path_length.md
99101
- Administration Examples:
100102
- webknossos-py/examples/user_times.md
101103
- webknossos-py/examples/annotation_project_administration.md
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Annotation File to OME-TIFF
2+
3+
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).
4+
5+
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).
6+
7+
```python
8+
--8<--
9+
webknossos/examples/load_annotation_from_file.py
10+
--8<--
11+
```
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Skeleton Path Length
2+
3+
This example shows how to downloaded a skeleton annotation from WEBKNOSSOS and calcuate the total path length along all edges of each tree.
4+
5+
```python
6+
--8<--
7+
webknossos/examples/skeleton_path_length.py
8+
--8<--
9+
```
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
from pathlib import Path
2+
3+
from tifffile import imwrite
4+
5+
import webknossos as wk
6+
7+
# Specify a bounding box for cutouts
8+
# (topleft_x, topleft_y, topleft_z), (width, height, depth)
9+
BOUNDING_BOX = wk.BoundingBox((0, 0, 0), (500, 500, 50))
10+
11+
12+
def load_annotation(annotation_file: Path) -> None:
13+
# Read the WEBKNOSSOS annotation file (a zipped WKW)
14+
annotation = wk.Annotation.load(annotation_file)
15+
16+
# Treat it as a regular WK volume layer
17+
with annotation.temporary_volume_layer_copy() as segmentation_layer:
18+
# Do any standard layer operation, e.g. reading a cutout as a numpy array
19+
mag_view = segmentation_layer.get_finest_mag()
20+
segments = mag_view.read(absolute_bounding_box=BOUNDING_BOX)
21+
22+
# Write segmentation IDs to an OME Tiff file
23+
imwrite(
24+
"segmentation.ome.tiff",
25+
segments.T, # note, the tiff lib use different channel order
26+
ome=True,
27+
metadata={
28+
"axes": "ZYXC",
29+
},
30+
)
31+
32+
33+
if __name__ == "__main__":
34+
# Path to annotation file on your computer
35+
ANNOTATION_FILE = Path("my_annotation_file.zip")
36+
37+
load_annotation(ANNOTATION_FILE)
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
from typing import Tuple
2+
3+
import numpy as np
4+
5+
import webknossos as wk
6+
7+
8+
def calculate_path_length(annotation_url: str, auth_token: str) -> None:
9+
with wk.webknossos_context(token=auth_token):
10+
# Download a annotation directly from the WEBKNOSSOS server
11+
annotation = wk.Annotation.download(
12+
annotation_url,
13+
)
14+
15+
skeleton = annotation.skeleton
16+
voxel_size = annotation.voxel_size
17+
18+
# Iterate over all the tree in a skeleton and calculate their path length
19+
for tree in skeleton.flattened_trees():
20+
path_length = calculate_path_length_for_tree(tree, voxel_size)
21+
22+
# Log the results :-)
23+
print(f"Tree {tree.name} has a path length of {path_length:.2f} nm")
24+
25+
26+
def calculate_path_length_for_tree(
27+
tree: wk.Tree, voxel_size: Tuple[float, float, float]
28+
) -> float:
29+
# Auxillary method calculate the maximum path length of a given tree
30+
# Assumes that the annotation does not contain any cycles
31+
32+
assert (
33+
len(tree.nodes) > 1
34+
), "Each tree should have at least two nodes to calculate the path length"
35+
result = 0
36+
37+
# Iterate over all edges
38+
for source_node, target_node in tree.edges:
39+
diff_vector = np.array(source_node.position) - np.array(target_node.position)
40+
scaled_diff_vector = diff_vector * voxel_size
41+
edge_length = np.sqrt(scaled_diff_vector.dot(scaled_diff_vector))
42+
result += edge_length
43+
44+
return result
45+
46+
47+
if __name__ == "__main__":
48+
# Authentication and API token for your account
49+
# Get it at https://webknossos.org/auth/token
50+
TOKEN = "YOUR-token"
51+
52+
# A WEBKNOSOS URL containing the skeleton annotation
53+
ANNOTATION_URL = "https://webknossos.org/annotations/12345678"
54+
55+
calculate_path_length(ANNOTATION_URL, TOKEN)
Binary file not shown.

webknossos/tests/test_examples.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,3 +326,25 @@ def test_download_tiff_stack() -> None:
326326
len(list(output_path.iterdir()))
327327
== mag_view.bounding_box.size.z / mag_view.mag.z
328328
)
329+
330+
331+
@pytest.mark.block_network(allowed_hosts=[".*"])
332+
@pytest.mark.vcr(ignore_hosts=["webknossos.org", "data-humerus.webknossos.org"])
333+
def test_skeleton_path_length() -> None:
334+
from examples.skeleton_path_length import calculate_path_length
335+
336+
# Public skeleton annotation by MH Lab
337+
annotation_id = "https://webknossos.org/annotations/62b191ef010000e80033e7c0"
338+
token = "123"
339+
calculate_path_length(annotation_id, token)
340+
341+
342+
def test_load_annotation_file() -> None:
343+
from examples.load_annotation_from_file import load_annotation
344+
345+
annotation_file = Path(
346+
"./tests/example_files/l4dense_motta_et_al_demo_v2__explorational.zip"
347+
).resolve()
348+
349+
with tmp_cwd():
350+
load_annotation(annotation_file)

0 commit comments

Comments
 (0)