Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
43 changes: 25 additions & 18 deletions tests/integration/converters/test_ome_tiff.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,32 @@
from tiledb.bioimg.openslide import TileDBOpenSlide


def test_ome_tiff_converter(tmp_path):
OMETiffConverter.to_tiledb(get_path("CMU-1-Small-Region.ome.tiff"), str(tmp_path))
@pytest.mark.parametrize("open_fileobj", [False, True])
def test_ome_tiff_converter(tmp_path, open_fileobj):
input_path = str(get_path("CMU-1-Small-Region.ome.tiff"))
output_path = str(tmp_path)
if open_fileobj:
with open(input_path, "rb") as f:
OMETiffConverter.to_tiledb(f, output_path)
else:
OMETiffConverter.to_tiledb(input_path, output_path)

t = TileDBOpenSlide.from_group_uri(str(tmp_path))
assert len(tiledb.Group(str(tmp_path))) == t.level_count == 2
with TileDBOpenSlide.from_group_uri(output_path) as t:
assert len(tiledb.Group(output_path)) == t.level_count == 2

schemas = (get_schema(2220, 2967), get_schema(574, 768))
assert t.dimensions == schemas[0].shape[:-3:-1]
for i in range(t.level_count):
assert t.level_dimensions[i] == schemas[i].shape[:-3:-1]
with tiledb.open(str(tmp_path / f"l_{i}.tdb")) as A:
assert A.schema == schemas[i]
schemas = (get_schema(2220, 2967), get_schema(574, 768))
assert t.dimensions == schemas[0].shape[:-3:-1]
for i in range(t.level_count):
assert t.level_dimensions[i] == schemas[i].shape[:-3:-1]
with tiledb.open(str(tmp_path / f"l_{i}.tdb")) as A:
assert A.schema == schemas[i]

region = t.read_region(level=0, location=(100, 100), size=(300, 400))
assert isinstance(region, np.ndarray)
assert region.ndim == 3
assert region.dtype == np.uint8
img = PIL.Image.fromarray(region)
assert img.size == (300, 400)
region = t.read_region(level=0, location=(100, 100), size=(300, 400))
assert isinstance(region, np.ndarray)
assert region.ndim == 3
assert region.dtype == np.uint8
img = PIL.Image.fromarray(region)
assert img.size == (300, 400)


def test_ome_tiff_converter_different_dtypes(tmp_path):
Expand Down Expand Up @@ -85,8 +92,8 @@ def test_ome_tiff_converter_artificial_rountrip(tmp_path, filename, dims, tiles)

OMETiffConverter.to_tiledb(input_path, str(tiledb_path), tiles=tiles)

t = TileDBOpenSlide.from_group_uri(str(tiledb_path))
assert len(tiledb.Group(str(tiledb_path))) == t.level_count == 1
with TileDBOpenSlide.from_group_uri(str(tiledb_path)) as t:
assert len(tiledb.Group(str(tiledb_path))) == t.level_count == 1

with tiledb.open(str(tiledb_path / "l_0.tdb")) as A:
assert "".join(dim.name for dim in A.domain) == dims
Expand Down
40 changes: 20 additions & 20 deletions tests/integration/converters/test_ome_zarr.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,20 @@ def test_ome_zarr_converter(tmp_path, series_idx):
with tiledb.open(str(tmp_path / "l_0.tdb")) as A:
assert A.schema == schema

t = TileDBOpenSlide.from_group_uri(str(tmp_path))
assert t.dimensions == t.level_dimensions[0] == schema.shape[:-3:-1]

region_location = (100, 100)
region_size = (300, 400)
region = t.read_region(level=0, location=region_location, size=region_size)
assert isinstance(region, np.ndarray)
assert region.ndim == 3
assert region.dtype == np.uint8
img = PIL.Image.fromarray(region)
assert img.size == (
min(t.dimensions[0] - region_location[0], region_size[0]),
min(t.dimensions[1] - region_location[1], region_size[1]),
)
with TileDBOpenSlide.from_group_uri(str(tmp_path)) as t:
assert t.dimensions == t.level_dimensions[0] == schema.shape[:-3:-1]

region_location = (100, 100)
region_size = (300, 400)
region = t.read_region(level=0, location=region_location, size=region_size)
assert isinstance(region, np.ndarray)
assert region.ndim == 3
assert region.dtype == np.uint8
img = PIL.Image.fromarray(region)
assert img.size == (
min(t.dimensions[0] - region_location[0], region_size[0]),
min(t.dimensions[1] - region_location[1], region_size[1]),
)


@pytest.mark.parametrize("series_idx", [0, 1, 2])
Expand Down Expand Up @@ -87,13 +87,13 @@ def test_ome_zarr_converter_incremental(tmp_path):
input_path = get_path("CMU-1-Small-Region.ome.zarr/0")

OMEZarrConverter.to_tiledb(input_path, str(tmp_path), level_min=1)
t = TileDBOpenSlide.from_group_uri(str(tmp_path))
assert len(tiledb.Group(str(tmp_path))) == t.level_count == 1
with TileDBOpenSlide.from_group_uri(str(tmp_path)) as t:
assert len(tiledb.Group(str(tmp_path))) == t.level_count == 1

OMEZarrConverter.to_tiledb(input_path, str(tmp_path), level_min=0)
t = TileDBOpenSlide.from_group_uri(str(tmp_path))
assert len(tiledb.Group(str(tmp_path))) == t.level_count == 2
with TileDBOpenSlide.from_group_uri(str(tmp_path)) as t:
assert len(tiledb.Group(str(tmp_path))) == t.level_count == 2

OMEZarrConverter.to_tiledb(input_path, str(tmp_path), level_min=0)
t = TileDBOpenSlide.from_group_uri(str(tmp_path))
assert len(tiledb.Group(str(tmp_path))) == t.level_count == 2
with TileDBOpenSlide.from_group_uri(str(tmp_path)) as t:
assert len(tiledb.Group(str(tmp_path))) == t.level_count == 2
22 changes: 11 additions & 11 deletions tests/integration/converters/test_openslide.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,16 @@ def test_openslide_converter(tmp_path):
assert A.schema == get_schema(2220, 2967)

o = openslide.open_slide(svs_path)
t = TileDBOpenSlide.from_group_uri(str(tmp_path))
with TileDBOpenSlide.from_group_uri(str(tmp_path)) as t:

assert t.level_count == o.level_count
assert t.dimensions == o.dimensions
assert t.level_dimensions == o.level_dimensions
assert t.level_downsamples == o.level_downsamples
assert t.level_count == o.level_count
assert t.dimensions == o.dimensions
assert t.level_dimensions == o.level_dimensions
assert t.level_downsamples == o.level_downsamples

region = t.read_region(level=0, location=(100, 100), size=(300, 400))
assert isinstance(region, np.ndarray)
assert region.ndim == 3
assert region.dtype == np.uint8
img = PIL.Image.fromarray(region)
assert img.size == (300, 400)
region = t.read_region(level=0, location=(100, 100), size=(300, 400))
assert isinstance(region, np.ndarray)
assert region.ndim == 3
assert region.dtype == np.uint8
img = PIL.Image.fromarray(region)
assert img.size == (300, 400)
6 changes: 3 additions & 3 deletions tests/unit/test_openslide.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@ def r():
A.meta["level"] = level
G.add(level_path)

tdb_os = TileDBOpenSlide.from_group_uri(group_path)
assert tdb_os.level_count == len(level_dimensions)
assert tdb_os.level_dimensions == tuple(level_dimensions)
with TileDBOpenSlide.from_group_uri(group_path) as t:
assert t.level_count == len(level_dimensions)
assert t.level_dimensions == tuple(level_dimensions)
37 changes: 20 additions & 17 deletions tiledb/bioimg/openslide.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from __future__ import annotations

from operator import itemgetter
from typing import Sequence, Tuple
from typing import Any, Sequence, Tuple

import numpy as np

Expand All @@ -11,10 +11,6 @@


class TileDBOpenSlide:
def __init__(self, level_info: Sequence[Tuple[str, int, int]]):
self._level_uris = tuple(map(itemgetter(0), level_info))
self._level_dims = tuple(map(itemgetter(1, 2), level_info)) # (width, height)

@classmethod
def from_group_uri(cls, uri: str) -> TileDBOpenSlide:
"""
Expand All @@ -24,15 +20,22 @@ def from_group_uri(cls, uri: str) -> TileDBOpenSlide:
with tiledb.Group(uri) as G:
level_info = []
for o in G:
uri = o.uri
with tiledb.open(uri) as a:
level = a.meta.get("level", 0)
width = a.shape[-1]
height = a.shape[-2]
level_info.append((level, uri, width, height))
array = tiledb.open(o.uri)
level = array.meta.get("level", 0)
level_info.append((level, array))
# sort by level
level_info.sort(key=itemgetter(0))
return cls(tuple(map(itemgetter(1, 2, 3), level_info)))
return cls(tuple(map(itemgetter(1), level_info)))

def __init__(self, level_arrays: Sequence[tiledb.Array]):
self._level_arrays = level_arrays

def __enter__(self) -> TileDBOpenSlide:
return self

def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
for array in self._level_arrays:
array.close()

@property
def level_count(self) -> int:
Expand All @@ -42,7 +45,7 @@ def level_count(self) -> int:

:return: The number of levels in the slide
"""
return len(self._level_dims)
return len(self._level_arrays)

@property
def dimensions(self) -> Tuple[int, int]:
Expand All @@ -57,7 +60,7 @@ def level_dimensions(self) -> Sequence[Tuple[int, int]]:

:return: A sequence of dimensions for each level
"""
return self._level_dims
return tuple((array.shape[-1], array.shape[-2]) for array in self._level_arrays)

@property
def level_downsamples(self) -> Sequence[float]:
Expand All @@ -84,9 +87,9 @@ def read_region(
x, y = location
w, h = size
dim_to_slice = {"X": slice(x, x + w), "Y": slice(y, y + h)}
with tiledb.open(self._level_uris[level]) as a:
dims = [dim.name for dim in a.domain]
image = a[tuple(dim_to_slice.get(dim, slice(None)) for dim in dims)]
array = self._level_arrays[level]
dims = [dim.name for dim in array.domain]
image = array[tuple(dim_to_slice.get(dim, slice(None)) for dim in dims)]
# transpose image to YXC
return transpose_array(image, dims, "YXC")

Expand Down