Skip to content

Commit

Permalink
investigate dropping ome-zarr dependency
Browse files Browse the repository at this point in the history
  • Loading branch information
will-moore committed Dec 13, 2024
1 parent 20853c2 commit 68b26bd
Show file tree
Hide file tree
Showing 3 changed files with 184 additions and 8 deletions.
16 changes: 9 additions & 7 deletions napari_ome_zarr/_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
from typing import Any, Dict, Iterator, List, Optional

import numpy as np
from ome_zarr.io import parse_url
from .ome_zarr_reader import read_ome_zarr
# from ome_zarr.io import parse_url
from ome_zarr.reader import Label, Node, Reader
from ome_zarr.types import LayerData, PathLike, ReaderFunction
from vispy.color import Colormap
Expand All @@ -32,12 +33,13 @@ def napari_get_reader(path: PathLike) -> Optional[ReaderFunction]:
if len(path) > 1:
warnings.warn("more than one path is not currently supported")
path = path[0]
zarr = parse_url(path)
if zarr:
reader = Reader(zarr)
return transform(reader())
# Ignoring this path
return None
return read_ome_zarr(path)
# zarr = parse_url(path)
# if zarr:
# reader = Reader(zarr)
# return transform(reader())
# # Ignoring this path
# return None


def transform_properties(
Expand Down
174 changes: 174 additions & 0 deletions napari_ome_zarr/ome_zarr_reader.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@


# zarr v3

import zarr
from zarr import Group
import numpy as np
import dask.array as da
from typing import List
from vispy.color import Colormap

from typing import Any, Callable, Dict, List, Tuple, Union

LayerData = Union[Tuple[Any], Tuple[Any, Dict], Tuple[Any, Dict, str]]


class Spec():

def __init__(self, group: Group):
self.group = group

@staticmethod
def matches(group: Group) -> bool:
return False

def data(self) -> List[da.core.Array] | None:
return None

def metadata(self) -> Dict[str, Any] | None:
# napari layer metadata
return {}

def children(self):
return []

def iter_nodes(self):
yield self
for child in self.children():
for ch in child.iter_nodes():
yield ch

def iter_data(self):
for node in self.iter_nodes():
data = node.data()
if data:
yield data

@staticmethod
def get_attrs(group: Group):
if "ome" in group.attrs:
return group.attrs["ome"]
return group.attrs


class Multiscales(Spec):

@staticmethod
def matches(group: Group) -> bool:
return "multiscales" in Spec.get_attrs(group)

def children(self):
ch = []
# test for child "labels"
try:
grp = self.group["labels"]
attrs = Spec.get_attrs(grp)
if "labels" in attrs:
for name in attrs["labels"]:
g = grp[name]
if Label.matches(g):
ch.append(Label(g))
except KeyError:
pass
return ch

def data(self):
attrs = Spec.get_attrs(self.group)
paths = [ds["path"] for ds in attrs["multiscales"][0]["datasets"]]
return [da.from_zarr(self.group[path]) for path in paths]

def metadata(self):
rsp = {}
attrs = Spec.get_attrs(self.group)
axes = attrs["multiscales"][0]["axes"]
atypes = [axis["type"] for axis in axes]
if "channel" in atypes:
channel_axis = atypes.index("channel")
rsp["channel_axis"] = channel_axis
if "omero" in attrs:
colormaps = []
for ch in attrs["omero"]["channels"]:
color = ch.get("color", None)
if color is not None:
rgb = [(int(color[i : i + 2], 16) / 255) for i in range(0, 6, 2)]
# colormap is range: black -> rgb color
colormaps.append(Colormap([[0, 0, 0], rgb]))
rsp["colormap"] = colormaps
return rsp

class Bioformats2raw(Spec):

@staticmethod
def matches(group: Group) -> bool:
attrs = Spec.get_attrs(group)
# Don't consider "plate" as a Bioformats2raw layout
return "bioformats2raw.layout" in attrs and "plate" not in attrs

def children(self):
# TDOO: lookup children from series of OME/METADATA.xml
childnames = ["0"]
rv = []
for name in childnames:
g = self.group[name]
if Multiscales.matches(g):
rv.append(Multiscales(g))
return rv


class Plate(Spec):

@staticmethod
def matches(group: Group) -> bool:
return "plate" in Spec.get_attrs(group)


class Label(Multiscales):

@staticmethod
def matches(group: Group) -> bool:
# label must also be Multiscales
if not Multiscales.matches(group):
return False
return "image-label" in Spec.get_attrs(group)

def metadata(self) -> Dict[str, Any] | None:
# override Multiscales metadata
return {}


def read_ome_zarr(url):

def f(*args: Any, **kwargs: Any) -> List[LayerData]:

results: List[LayerData] = list()

# TODO: handle missing file
root_group = zarr.open(url)

print("Root group", root_group.attrs.asdict())

if Bioformats2raw.matches(root_group):
spec = Bioformats2raw(root_group)
elif Multiscales.matches(root_group):
spec = Multiscales(root_group)
elif Plate.matches(root_group):
spec = Plate(root_group)

if spec:
print("spec", spec)
nodes = list(spec.iter_nodes())
print("Nodes", nodes)
for node in nodes:
node_data = node.data()
metadata = node.metadata()
# print(Spec.get_attrs(node.group))
if Label.matches(node.group):
rv: LayerData = (node_data, metadata, "labels")
else:
rv: LayerData = (node_data, metadata)
results.append(rv)

return results

return f
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ python_requires = >=3.9
setup_requires = setuptools_scm
# add your package requirements here
install_requires =
ome-zarr>=0.3.0,!=0.8.3
zarr==v3.0.0-beta.3
numpy
vispy
napari>=0.4.13
Expand Down

0 comments on commit 68b26bd

Please sign in to comment.