Skip to content

Commit a144b3b

Browse files
authored
Add method to Dataset API to copy an existing layer from a different dataset (#345)
* implement add_copy_layer * update changelog
1 parent 4278a85 commit a144b3b

File tree

3 files changed

+71
-0
lines changed

3 files changed

+71
-0
lines changed

Changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ For upgrade instructions, please check the respective *Breaking Changes* section
1212
### Breaking Changes in Config & CLI
1313

1414
### Added
15+
- Added `add_copy_layer()` to `wkcuber.api.dataset.Dataset` to copy the layer of a different dataset. [#345](https://github.com/scalableminds/webknossos-cuber/pull/345)
1516

1617
### Changed
1718

tests/test_dataset.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from wkw import wkw
1515
from wkw.wkw import WKWException
1616

17+
from wkcuber.api.bounding_box import BoundingBox
1718
from wkcuber.api.dataset import Dataset
1819
from os import makedirs
1920

@@ -1439,3 +1440,37 @@ def test_dataset_name(tmp_path: Path) -> None:
14391440
tmp_path / "some_new_name", scale=(1, 1, 1), name="very important dataset"
14401441
)
14411442
assert ds2.name == "very important dataset"
1443+
1444+
1445+
def test_add_copy_layer(tmp_path: Path) -> None:
1446+
ds = Dataset.create(tmp_path / "ds", scale=(2, 2, 1))
1447+
1448+
# Create dataset to copy data from
1449+
other_ds = Dataset.create(tmp_path / "other_ds", scale=(2, 2, 1))
1450+
original_color_layer = other_ds.add_layer("color", LayerCategories.COLOR_TYPE)
1451+
original_color_layer.add_mag(1).write(
1452+
offset=(10, 20, 30), data=(np.random.rand(32, 64, 128) * 255).astype(np.uint8)
1453+
)
1454+
1455+
# Copies the "color" layer from a different dataset
1456+
ds.add_copy_layer(tmp_path / "other_ds" / "color")
1457+
assert len(ds.layers) == 1
1458+
color_layer = ds.get_layer("color")
1459+
assert color_layer.get_bounding_box() == BoundingBox(
1460+
topleft=(10, 20, 30), size=(32, 64, 128)
1461+
)
1462+
assert color_layer.mags.keys() == original_color_layer.mags.keys()
1463+
assert len(color_layer.mags.keys()) >= 1
1464+
for mag in color_layer.mags.keys():
1465+
np.array_equal(
1466+
color_layer.get_mag(mag).read(), original_color_layer.get_mag(mag).read()
1467+
)
1468+
# Test if the copied layer contains actual data
1469+
assert np.max(color_layer.get_mag(mag).read()) > 0
1470+
1471+
with pytest.raises(IndexError):
1472+
# The dataset already has a layer called "color".
1473+
ds.add_copy_layer(tmp_path / "other_ds" / "color")
1474+
1475+
# Test if the changes of the properties are persisted on disk by opening it again
1476+
assert "color" in Dataset(tmp_path / "ds").layers.keys()

wkcuber/api/dataset.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import operator
2+
import shutil
23
from argparse import Namespace
34
from shutil import rmtree
45
from os import makedirs
@@ -442,6 +443,40 @@ def add_symlink_layer(self, foreign_layer_path: Union[str, Path]) -> Layer:
442443
self.get_layer(layer_name)._setup_mag(resolution.mag.to_layer_name())
443444
return self.layers[layer_name]
444445

446+
def add_copy_layer(self, foreign_layer_path: Union[str, Path]) -> Layer:
447+
"""
448+
Copies the data at `foreign_layer_path` which belongs to another dataset to the current dataset.
449+
Additionally, the relevant information from the `datasource-properties.json` of the other dataset are copied too.
450+
"""
451+
452+
foreign_layer_path = Path(os.path.abspath(foreign_layer_path))
453+
layer_name = foreign_layer_path.name
454+
if layer_name in self.layers.keys():
455+
raise IndexError(
456+
f"Cannot copy {foreign_layer_path}. This dataset already has a layer called {layer_name}."
457+
)
458+
459+
shutil.copytree(foreign_layer_path, join(self.path, layer_name))
460+
461+
# copy the properties of the layer into the properties of this dataset
462+
layer_properties = Dataset(foreign_layer_path.parent).properties.data_layers[
463+
layer_name
464+
]
465+
self.properties.data_layers[layer_name] = layer_properties
466+
self.properties._export_as_json()
467+
468+
self._layers[layer_name] = self._create_layer(
469+
layer_name,
470+
_dtype_per_layer_to_dtype_per_channel(
471+
layer_properties.element_class, layer_properties.num_channels
472+
),
473+
layer_properties.num_channels,
474+
layer_properties.category,
475+
)
476+
for resolution in layer_properties.wkw_magnifications:
477+
self.get_layer(layer_name)._setup_mag(resolution.mag.to_layer_name())
478+
return self.layers[layer_name]
479+
445480
def copy_dataset(
446481
self,
447482
new_dataset_path: Union[str, Path],

0 commit comments

Comments
 (0)