|
4 | 4 |
|
5 | 5 | import numpy as np
|
6 | 6 | import pytest
|
| 7 | +from cluster_tools import get_executor |
7 | 8 |
|
8 | 9 | import webknossos as wk
|
| 10 | +from webknossos import Annotation, SegmentationLayer |
| 11 | +from webknossos.annotation.volume_layer import VolumeLayerEditMode |
9 | 12 | from webknossos.dataset import DataFormat
|
10 | 13 | from webknossos.geometry import BoundingBox, Vec3Int
|
11 | 14 |
|
@@ -40,7 +43,10 @@ def test_annotation_from_wkw_zip_file() -> None:
|
40 | 43 | assert len(list(copied_annotation.get_volume_layer_names())) == 1
|
41 | 44 | assert len(list(copied_annotation.skeleton.flattened_trees())) == 1
|
42 | 45 |
|
43 |
| - copied_annotation.add_volume_layer(name="new_volume_layer") |
| 46 | + copied_annotation.add_volume_layer( |
| 47 | + name="new_volume_layer", |
| 48 | + dtype=np.uint32, |
| 49 | + ) |
44 | 50 | assert len(list(copied_annotation.get_volume_layer_names())) == 2
|
45 | 51 | copied_annotation.delete_volume_layer(volume_layer_name="new_volume_layer")
|
46 | 52 | assert len(list(copied_annotation.get_volume_layer_names())) == 1
|
@@ -368,3 +374,158 @@ def test_tree_metadata(tmp_path: Path) -> None:
|
368 | 374 | list(tmp_annotation.skeleton.flattened_trees())[0].metadata["test_tree"]
|
369 | 375 | == "test"
|
370 | 376 | )
|
| 377 | + |
| 378 | + |
| 379 | +@pytest.mark.parametrize( |
| 380 | + "edit_mode", [VolumeLayerEditMode.MEMORY, VolumeLayerEditMode.TEMPORARY_DIRECTORY] |
| 381 | +) |
| 382 | +@pytest.mark.parametrize("executor", ["sequential", "multiprocessing"]) |
| 383 | +def test_edit_volume_annotation(edit_mode: VolumeLayerEditMode, executor: str) -> None: |
| 384 | + dtype = np.uint32 |
| 385 | + data = np.ones((1, 10, 10, 10), dtype=dtype) |
| 386 | + ann = wk.Annotation( |
| 387 | + name="my_annotation", |
| 388 | + dataset_name="sample_dataset", |
| 389 | + voxel_size=(11.2, 11.2, 25.0), |
| 390 | + ) |
| 391 | + |
| 392 | + volume_layer = ann.add_volume_layer( |
| 393 | + name="segmentation", |
| 394 | + dtype=dtype, |
| 395 | + ) |
| 396 | + if edit_mode == VolumeLayerEditMode.MEMORY and executor == "multiprocessing": |
| 397 | + with pytest.raises(ValueError, match="SequentialExecutor"): |
| 398 | + with volume_layer.edit( |
| 399 | + edit_mode=edit_mode, executor=get_executor(executor) |
| 400 | + ) as seg_layer: |
| 401 | + pass |
| 402 | + else: |
| 403 | + with volume_layer.edit( |
| 404 | + edit_mode=edit_mode, executor=get_executor(executor) |
| 405 | + ) as seg_layer: |
| 406 | + assert isinstance(seg_layer, SegmentationLayer) |
| 407 | + mag = seg_layer.add_mag(1) |
| 408 | + mag.write(data, absolute_offset=(0, 0, 0), allow_resize=True) |
| 409 | + with volume_layer.edit(edit_mode=edit_mode) as seg_layer: |
| 410 | + assert len(seg_layer.mags) == 1 |
| 411 | + mag = seg_layer.get_mag(1) |
| 412 | + read_data = mag.read(absolute_offset=(0, 0, 0), size=(10, 10, 10)) |
| 413 | + assert np.array_equal(data, read_data) |
| 414 | + |
| 415 | + |
| 416 | +def test_edited_volume_annotation_format() -> None: |
| 417 | + import zipfile |
| 418 | + |
| 419 | + import tensorstore |
| 420 | + |
| 421 | + path = TESTDATA_DIR / "annotations" / "l4_sample__explorational__suser__94b271.zip" |
| 422 | + ann = Annotation.load(path) |
| 423 | + data = np.ones(shape=(10, 10, 10)) |
| 424 | + |
| 425 | + volume_layer = ann.add_volume_layer( |
| 426 | + name="segmentation", |
| 427 | + dtype=np.uint32, |
| 428 | + ) |
| 429 | + with volume_layer.edit() as seg_layer: |
| 430 | + mag_view = seg_layer.add_mag(1) |
| 431 | + mag_view.write(data, allow_resize=True) |
| 432 | + |
| 433 | + save_path = TESTOUTPUT_DIR / "saved_annotation.zip" |
| 434 | + ann.save(save_path) |
| 435 | + unpack_dir = TESTOUTPUT_DIR / "unpacked_annotation" |
| 436 | + with zipfile.ZipFile(save_path, "r") as zip_ref: |
| 437 | + zip_ref.extractall(unpack_dir) |
| 438 | + |
| 439 | + # test for the format assumptions as mentioned in https://github.com/scalableminds/webknossos/issues/8604 |
| 440 | + ts = tensorstore.open( |
| 441 | + { |
| 442 | + "driver": "zarr3", |
| 443 | + "kvstore": { |
| 444 | + "driver": "zip", |
| 445 | + "path": "volumeAnnotationData/1/", |
| 446 | + "base": { |
| 447 | + "driver": "file", |
| 448 | + "path": str(unpack_dir / "data_1_segmentation.zip"), |
| 449 | + }, |
| 450 | + }, |
| 451 | + }, |
| 452 | + create=False, |
| 453 | + open=True, |
| 454 | + ).result() |
| 455 | + metadata = ts.spec().to_json()["metadata"] |
| 456 | + |
| 457 | + assert metadata["chunk_key_encoding"] == { |
| 458 | + "configuration": {"separator": "."}, |
| 459 | + "name": "default", |
| 460 | + } |
| 461 | + assert ["transpose", "bytes", "blosc"] == [ |
| 462 | + codec["name"] for codec in metadata["codecs"] |
| 463 | + ] |
| 464 | + data_read = ts.read().result()[0, :10, :10, :10] |
| 465 | + assert np.array_equal(data, data_read) |
| 466 | + |
| 467 | + |
| 468 | +@pytest.mark.parametrize( |
| 469 | + "edit_mode", [VolumeLayerEditMode.MEMORY, VolumeLayerEditMode.TEMPORARY_DIRECTORY] |
| 470 | +) |
| 471 | +def test_edited_volume_annotation_save_load(edit_mode: VolumeLayerEditMode) -> None: |
| 472 | + data = np.ones((1, 10, 10, 10)) |
| 473 | + |
| 474 | + ann = wk.Annotation( |
| 475 | + name="my_annotation", |
| 476 | + dataset_name="sample_dataset", |
| 477 | + voxel_size=(11.2, 11.2, 25.0), |
| 478 | + ) |
| 479 | + |
| 480 | + volume_layer = ann.add_volume_layer(name="segmentation", dtype=np.uint32) |
| 481 | + with volume_layer.edit(edit_mode=edit_mode) as seg_layer: |
| 482 | + mag_view = seg_layer.add_mag(1) |
| 483 | + mag_view.write(data, allow_resize=True) |
| 484 | + |
| 485 | + save_path = TESTOUTPUT_DIR / "annotation_saved.zip" |
| 486 | + ann.save(save_path) |
| 487 | + ann_loaded = Annotation.load(save_path) |
| 488 | + |
| 489 | + volume_layer_downloaded = ann_loaded.get_volume_layer("segmentation") |
| 490 | + |
| 491 | + with volume_layer_downloaded.edit(edit_mode=edit_mode) as seg_layer: |
| 492 | + assert len(seg_layer.mags) == 1 |
| 493 | + mag = seg_layer.get_mag(1) |
| 494 | + read_data = mag.read(absolute_offset=(0, 0, 0), size=(10, 10, 10)) |
| 495 | + assert np.array_equal(data, read_data) |
| 496 | + |
| 497 | + |
| 498 | +@pytest.mark.use_proxay |
| 499 | +def test_edited_volume_annotation_upload_download() -> None: |
| 500 | + data = np.ones((1, 10, 10, 10)) |
| 501 | + |
| 502 | + ann = Annotation.load( |
| 503 | + TESTDATA_DIR / "annotations" / "l4_sample__explorational__suser__94b271.zip" |
| 504 | + ) |
| 505 | + ann.organization_id = "Organization_X" |
| 506 | + |
| 507 | + volume_layer = ann.add_volume_layer( |
| 508 | + name="segmentation", |
| 509 | + dtype=np.uint32, |
| 510 | + ) |
| 511 | + with volume_layer.edit() as seg_layer: |
| 512 | + mag_view = seg_layer.add_mag(1) |
| 513 | + mag_view.write(data, allow_resize=True) |
| 514 | + |
| 515 | + url = ann.upload() |
| 516 | + ann_downloaded = Annotation.download( |
| 517 | + url, |
| 518 | + ) |
| 519 | + |
| 520 | + assert {layer.name for layer in ann_downloaded._volume_layers} == { |
| 521 | + "Volume", |
| 522 | + "segmentation", |
| 523 | + } |
| 524 | + |
| 525 | + volume_layer_downloaded = ann_downloaded.get_volume_layer("segmentation") |
| 526 | + |
| 527 | + with volume_layer_downloaded.edit() as seg_layer: |
| 528 | + assert len(seg_layer.mags) == 1 |
| 529 | + mag = seg_layer.get_mag(1) |
| 530 | + read_data = mag.read(absolute_offset=(0, 0, 0), size=(10, 10, 10)) |
| 531 | + assert np.array_equal(data, read_data) |
0 commit comments