Skip to content
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
007243d
make VolumeLayer non-protected
brokkoli71 Jul 16, 2025
f7deda1
add VolumeLayer.edit contextmanager
brokkoli71 Jul 22, 2025
69daad3
Merge branch 'master' into create-volume-annotations
brokkoli71 Jul 22, 2025
9e80718
format
brokkoli71 Jul 22, 2025
cd63d9f
lint
brokkoli71 Jul 22, 2025
96cfa07
add @pytest.mark.use_proxay
brokkoli71 Jul 22, 2025
6481415
format
brokkoli71 Jul 22, 2025
a7e8211
Merge branch 'refs/heads/master' into create-volume-annotations
brokkoli71 Jul 29, 2025
c6cf61e
Merge branch 'refs/heads/master' into create-volume-annotations
brokkoli71 Jul 30, 2025
892773a
rechunk mags with Zarr3Config to have expected volume annotations format
brokkoli71 Jul 30, 2025
d85c715
Enum for VolumeLayerEditMode
brokkoli71 Jul 30, 2025
487a1c5
test_edit_volume_annotation, test_save_edited_volume_annotation and t…
brokkoli71 Jul 30, 2025
393e9ee
format and lint
brokkoli71 Jul 30, 2025
f76231e
Merge branch 'master' into create-volume-annotations
brokkoli71 Jul 30, 2025
82cc58c
edited annotation uploadable
brokkoli71 Aug 9, 2025
88c5220
Merge branch 'master' into create-volume-annotations
brokkoli71 Aug 9, 2025
32542c1
set zarr3 datatype for wk.Annotation.download
brokkoli71 Aug 12, 2025
cab2679
implement VolumeLayerEditMode.MEMORY
brokkoli71 Aug 12, 2025
70f3b0d
get voxel_size from nml
brokkoli71 Aug 12, 2025
e7871d6
init zip when creating volume layer
brokkoli71 Aug 17, 2025
94e1626
store volume layer in zip of zips and save downloaded annotation volu…
brokkoli71 Aug 29, 2025
7b7e6fa
use volume_layers_root argument of Annotation.download
brokkoli71 Aug 29, 2025
c9bc2ca
Merge branch 'master' into create-volume-annotations
brokkoli71 Aug 29, 2025
b44b14a
update cassettes
brokkoli71 Aug 29, 2025
15d526b
load editing layer into tensorstore, as MemoryFileSystem is not compa…
brokkoli71 Aug 31, 2025
c103a9f
typing and format
brokkoli71 Aug 31, 2025
286fd80
avoid tensorstore array memory path collision
brokkoli71 Aug 31, 2025
bbfc7a3
rename nml to skeleton in offline_merger_mode.py
brokkoli71 Sep 5, 2025
ff05931
replace volume_layers_root with temp dir and incorporate further review
brokkoli71 Sep 5, 2025
635dcc0
Merge branch 'master' into create-volume-annotations
brokkoli71 Sep 5, 2025
cd58e96
fix format mistakes
brokkoli71 Sep 5, 2025
3b5fb00
update cassettes
brokkoli71 Sep 5, 2025
f628748
extract VolumeLayer to volume_layer.py
brokkoli71 Sep 5, 2025
aa66ed8
update cassette
brokkoli71 Sep 5, 2025
76bf92a
faster VolumeLayer.edit with SequentialExecutor
brokkoli71 Sep 5, 2025
93e9272
improve test_edit_volume_annotation
brokkoli71 Sep 5, 2025
e8c157c
document changes
brokkoli71 Sep 5, 2025
e77a40f
update learned_segmenter.py to use VolumeLayer.edit
brokkoli71 Sep 6, 2025
e56cc7e
lint
brokkoli71 Sep 6, 2025
ccdb188
VolumeLayer.edit kw-only
brokkoli71 Sep 15, 2025
533f7de
Merge branch 'master' into create-volume-annotations
brokkoli71 Sep 15, 2025
579d311
Merge branch 'master' into create-volume-annotations
brokkoli71 Sep 23, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

127 changes: 126 additions & 1 deletion webknossos/tests/test_annotation.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import pytest

import webknossos as wk
from webknossos import SegmentationLayer
from webknossos.annotation.annotation import VolumeLayerEditMode
from webknossos.dataset import DataFormat
from webknossos.geometry import BoundingBox, Vec3Int

Expand Down Expand Up @@ -40,7 +42,7 @@ def test_annotation_from_wkw_zip_file() -> None:
assert len(list(copied_annotation.get_volume_layer_names())) == 1
assert len(list(copied_annotation.skeleton.flattened_trees())) == 1

copied_annotation.add_volume_layer(name="new_volume_layer")
copied_annotation.create_volume_layer(name="new_volume_layer")
assert len(list(copied_annotation.get_volume_layer_names())) == 2
copied_annotation.delete_volume_layer(volume_layer_name="new_volume_layer")
assert len(list(copied_annotation.get_volume_layer_names())) == 1
Expand Down Expand Up @@ -368,3 +370,126 @@ def test_tree_metadata(tmp_path: Path) -> None:
list(tmp_annotation.skeleton.flattened_trees())[0].metadata["test_tree"]
== "test"
)


# TODO this is very slow
@pytest.mark.parametrize(
"edit_mode", [VolumeLayerEditMode.MEMORY, VolumeLayerEditMode.TEMPORARY_DIRECTORY]
)
def test_edit_volume_annotation(edit_mode: VolumeLayerEditMode) -> None:
dtype = np.uint32
data = np.ones((1, 10, 10, 10), dtype=dtype)
ann = wk.Annotation(
name="my_annotation",
dataset_name="sample_dataset",
voxel_size=(11.2, 11.2, 25.0),
)

volume_layer = ann.create_volume_layer(
name="segmentation",
zip_path=TESTOUTPUT_DIR / "test_volume_annotations.zip",
dtype=dtype,
)
with volume_layer.edit(edit_mode) as seg_layer:
assert isinstance(seg_layer, SegmentationLayer)
mag = seg_layer.add_mag(1)
mag.write(data, absolute_offset=(0, 0, 0), allow_resize=True)
with volume_layer.edit(edit_mode) as seg_layer:
assert len(seg_layer.mags) == 1
mag = seg_layer.get_mag(1)
read_data = mag.read(absolute_offset=(0, 0, 0), size=(10, 10, 10))
assert np.array_equal(data, read_data)


def test_edited_volume_annotation_format() -> None:
import zipfile

import tensorstore

path = TESTDATA_DIR / "annotations" / "l4_sample__explorational__suser__94b271.zip"
ann = wk.Annotation.load(path)
data = np.ones(shape=(10, 10, 10))

volume_layer = ann.create_volume_layer(
name="segmentation",
zip_path=TESTOUTPUT_DIR / "test_volume_annotations.zip",
dtype=np.uint32,
)
with volume_layer.edit() as seg_layer:
mag_view = seg_layer.add_mag(1)
mag_view.write(data, allow_resize=True)

export_path = TESTOUTPUT_DIR / "export_volume_layer.zip"
ann.save(export_path)
unpack_dir = TESTOUTPUT_DIR / "unpacked_volume_layer"
with zipfile.ZipFile(export_path, "r") as zip_ref:
zip_ref.extractall(unpack_dir)

# test for the format assumptions as mentioned in https://github.com/scalableminds/webknossos/issues/8604
ts = tensorstore.open(
{
"driver": "zarr3",
"kvstore": {
"driver": "zip",
"path": "volumeAnnotationData/1/",
"base": {
"driver": "file",
"path": str(unpack_dir / "data_1_segmentation.zip"),
},
},
},
create=False,
open=True,
).result()
metadata = ts.spec().to_json()["metadata"]

assert metadata["chunk_key_encoding"] == {
"configuration": {"separator": "."},
"name": "default",
}
assert ["transpose", "bytes", "blosc"] == [
codec["name"] for codec in metadata["codecs"]
]
data_read = ts.read().result()[0, :10, :10, :10]
assert np.array_equal(data, data_read)


@pytest.mark.use_proxay
def test_edited_volume_annotation_upload_download() -> None:
data = np.ones((1, 10, 10, 10))

ann = wk.Annotation.load(
TESTDATA_DIR / "annotations" / "l4_sample__explorational__suser__94b271.zip"
)
ann.organization_id = "Organization_X"
volume_layer = ann.create_volume_layer(
name="segmentation", zip_path=TESTOUTPUT_DIR / "test_volume_annotations.zip"
)
with volume_layer.edit() as seg_layer:
mag_view = seg_layer.add_mag(1)
mag_view.write(data, allow_resize=True)
# url = ann.upload()
#
# ann_downloaded = wk.Annotation.download(url)
# assert {layer.name for layer in ann_downloaded.volume_layers} == {
# "Volume",
# "segmentation",
# }
# ann_downloaded.save("/home/hannes/Downloads/tes2tss.zip")

# TODO this fails. the read data is all 0 and has offset
# ds = ann_downloaded.get_remote_annotation_dataset()
# mag_view = ds.layers["segmentation"].get_mag("1")
# assert mag_view.bounding_box == BoundingBox((0,0,0), (10, 10, 10))
# read_data = mag_view.read(absolute_offset=(0, 0, 0), size=(10, 10, 10))
# assert np.array_equal(data, read_data)

# todo change this back to ann_downloaded
with ann.temporary_volume_layer_copy(
volume_layer_name="segmentation"
) as read_layer:
read_data = read_layer.get_finest_mag().read(
absolute_offset=(0, 0, 0),
size=(10, 10, 10),
)
assert np.array_equal(data, read_data)
5 changes: 3 additions & 2 deletions webknossos/tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -659,15 +659,16 @@ def test_merge_fallback_no_fallback_layer(
),
)

annotation._volume_layers = [
webknossos.annotation._VolumeLayer( # type: ignore
annotation.volume_layers = [
webknossos.annotation.VolumeLayer( # type: ignore
id=0,
name=tmp_layer.name,
fallback_layer_name=fallback_mag.layer.name,
zip=volume_layer_zip,
segments={},
data_format=DataFormat.WKW,
largest_segment_id=largest_segment_id,
voxel_size=tmp_dataset.voxel_size,
),
]

Expand Down
2 changes: 1 addition & 1 deletion webknossos/webknossos/annotation/_nml_conversion.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ def annotation_to_nml(
format=str(volume.data_format),
largest_segment_id=volume.largest_segment_id,
)
for volume in annotation._volume_layers
for volume in annotation.volume_layers
]

meta = [
Expand Down
Loading
Loading