- pyproject towards PEP621
- catch errors reading metadata in tifread - read dimension order from ome
This commit is contained in:
@@ -6,9 +6,10 @@ from pathlib import Path
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import tifffile
|
import tifffile
|
||||||
import yaml
|
import yaml
|
||||||
from ome_types import model
|
import warnings
|
||||||
|
from ome_types import model, from_xml
|
||||||
|
|
||||||
from .. import AbstractReader
|
from .. import AbstractReader, try_default
|
||||||
|
|
||||||
|
|
||||||
class Reader(AbstractReader, ABC):
|
class Reader(AbstractReader, ABC):
|
||||||
@@ -25,61 +26,75 @@ class Reader(AbstractReader, ABC):
|
|||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def metadata(self):
|
def metadata(self):
|
||||||
return {key: yaml.safe_load(value) if isinstance(value, str) else value
|
return {key: try_default(yaml.safe_load, value, value) if isinstance(value, str) else value
|
||||||
for key, value in self.reader.imagej_metadata.items()}
|
for key, value in self.reader.imagej_metadata.items()}
|
||||||
|
|
||||||
def get_ome(self):
|
def get_ome(self):
|
||||||
page = self.reader.pages[0]
|
if self.reader.is_ome:
|
||||||
size_y = page.imagelength
|
with warnings.catch_warnings():
|
||||||
size_x = page.imagewidth
|
warnings.simplefilter('ignore', category=UserWarning)
|
||||||
if self.p_ndim == 3:
|
return from_xml(self.reader.ome_metadata)
|
||||||
size_c = page.samplesperpixel
|
|
||||||
size_t = self.metadata.get('frames', 1) # // C
|
|
||||||
else:
|
else:
|
||||||
size_c = self.metadata.get('channels', 1)
|
page = self.reader.pages[0]
|
||||||
size_t = self.metadata.get('frames', 1)
|
size_y = page.imagelength
|
||||||
size_z = self.metadata.get('slices', 1)
|
size_x = page.imagewidth
|
||||||
if 282 in page.tags and 296 in page.tags and page.tags[296].value == 1:
|
if self.p_ndim == 3:
|
||||||
f = page.tags[282].value
|
size_c = page.samplesperpixel
|
||||||
pxsize = f[1] / f[0]
|
size_t = self.metadata.get('frames', 1) # // C
|
||||||
else:
|
else:
|
||||||
pxsize = None
|
size_c = self.metadata.get('channels', 1)
|
||||||
|
size_t = self.metadata.get('frames', 1)
|
||||||
|
size_z = self.metadata.get('slices', 1)
|
||||||
|
if 282 in page.tags and 296 in page.tags and page.tags[296].value == 1:
|
||||||
|
f = page.tags[282].value
|
||||||
|
pxsize = f[1] / f[0]
|
||||||
|
else:
|
||||||
|
pxsize = None
|
||||||
|
|
||||||
dtype = page.dtype.name
|
dtype = page.dtype.name
|
||||||
if dtype not in ('int8', 'int16', 'int32', 'uint8', 'uint16', 'uint32',
|
if dtype not in ('int8', 'int16', 'int32', 'uint8', 'uint16', 'uint32',
|
||||||
'float', 'double', 'complex', 'double-complex', 'bit'):
|
'float', 'double', 'complex', 'double-complex', 'bit'):
|
||||||
dtype = 'float'
|
dtype = 'float'
|
||||||
|
|
||||||
interval_t = self.metadata.get('interval', 0)
|
interval_t = self.metadata.get('interval', 0)
|
||||||
|
|
||||||
ome = model.OME()
|
ome = model.OME()
|
||||||
ome.instruments.append(model.Instrument(id='Instrument:0'))
|
ome.instruments.append(model.Instrument(id='Instrument:0'))
|
||||||
ome.instruments[0].objectives.append(model.Objective(id='Objective:0'))
|
ome.instruments[0].objectives.append(model.Objective(id='Objective:0'))
|
||||||
ome.images.append(
|
ome.images.append(
|
||||||
model.Image(
|
model.Image(
|
||||||
id='Image:0',
|
id='Image:0',
|
||||||
pixels=model.Pixels(
|
pixels=model.Pixels(
|
||||||
id='Pixels:0',
|
id='Pixels:0',
|
||||||
size_c=size_c, size_z=size_z, size_t=size_t, size_x=size_x, size_y=size_y,
|
size_c=size_c, size_z=size_z, size_t=size_t, size_x=size_x, size_y=size_y,
|
||||||
dimension_order='XYCZT', type=dtype, # type: ignore
|
dimension_order='XYCZT', type=dtype, # type: ignore
|
||||||
physical_size_x=pxsize, physical_size_y=pxsize),
|
physical_size_x=pxsize, physical_size_y=pxsize),
|
||||||
objective_settings=model.ObjectiveSettings(id='Objective:0')))
|
objective_settings=model.ObjectiveSettings(id='Objective:0')))
|
||||||
for c, z, t in product(range(size_c), range(size_z), range(size_t)):
|
for c, z, t in product(range(size_c), range(size_z), range(size_t)):
|
||||||
ome.images[0].pixels.planes.append(model.Plane(the_c=c, the_z=z, the_t=t, delta_t=interval_t * t))
|
ome.images[0].pixels.planes.append(model.Plane(the_c=c, the_z=z, the_t=t, delta_t=interval_t * t))
|
||||||
return ome
|
return ome
|
||||||
|
|
||||||
def open(self):
|
def open(self):
|
||||||
self.reader = tifffile.TiffFile(self.path)
|
self.reader = tifffile.TiffFile(self.path)
|
||||||
page = self.reader.pages[0]
|
page = self.reader.pages.first
|
||||||
self.p_ndim = page.ndim # noqa
|
self.p_ndim = page.ndim # noqa
|
||||||
if self.p_ndim == 3:
|
if self.p_ndim == 3:
|
||||||
self.p_transpose = [i for i in [page.axes.find(j) for j in 'SYX'] if i >= 0] # noqa
|
self.p_transpose = [i for i in [page.axes.find(j) for j in 'SYX'] if i >= 0] # noqa
|
||||||
|
else:
|
||||||
|
self.p_transpose = [i for i in [page.axes.find(j) for j in 'YX'] if i >= 0] # noqa
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
self.reader.close()
|
self.reader.close()
|
||||||
|
|
||||||
def __frame__(self, c, z, t):
|
def __frame__(self, c: int, z: int, t: int):
|
||||||
|
dimension_order = self.ome.images[0].pixels.dimension_order.value
|
||||||
if self.p_ndim == 3:
|
if self.p_ndim == 3:
|
||||||
return np.transpose(self.reader.asarray(z + t * self.base_shape['z']), self.p_transpose)[c]
|
axes = ''.join([ax.lower() for ax in dimension_order if ax.lower() in 'zt'])
|
||||||
|
ct = {'z': z, 't': t}
|
||||||
|
n = sum([ct[ax] * np.prod(self.base_shape[axes[:i]]) for i, ax in enumerate(axes)])
|
||||||
|
return np.transpose(self.reader.asarray(int(n)), self.p_transpose)[int(c)]
|
||||||
else:
|
else:
|
||||||
return self.reader.asarray(c + z * self.base_shape['c'] + t * self.base_shape['c'] * self.base_shape['z'])
|
axes = ''.join([ax.lower() for ax in dimension_order if ax.lower() in 'czt'])
|
||||||
|
czt = {'c': c, 'z': z, 't': t}
|
||||||
|
n = sum([czt[ax] * np.prod(self.base_shape[axes[:i]]) for i, ax in enumerate(axes)])
|
||||||
|
return np.transpose(self.reader.asarray(int(n)), self.p_transpose)
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
#Insight Transform File V1.0
|
#Insight Transform File V1.0
|
||||||
#Transform 0
|
|
||||||
Transform: CompositeTransform_double_2_2
|
|
||||||
#Transform 1
|
#Transform 1
|
||||||
Transform: AffineTransform_double_2_2
|
Transform: AffineTransform_double_2_2
|
||||||
Parameters: 1 0 0 1 0 0
|
Parameters: 1 0 0 1 0 0
|
||||||
|
|||||||
@@ -299,7 +299,7 @@ class Transform:
|
|||||||
new.shape = [float(t) for t in transform['Size']]
|
new.shape = [float(t) for t in transform['Size']]
|
||||||
new.origin = [(t - 1) / 2 for t in new.shape]
|
new.origin = [(t - 1) / 2 for t in new.shape]
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError(f'{kind} tranforms not implemented (yet)')
|
raise NotImplementedError(f'{kind} transforms not implemented (yet)')
|
||||||
new.dparameters = 6 * [np.nan]
|
new.dparameters = 6 * [np.nan]
|
||||||
return new
|
return new
|
||||||
|
|
||||||
|
|||||||
@@ -1,46 +1,43 @@
|
|||||||
[tool.poetry]
|
[project]
|
||||||
name = "ndbioimage"
|
name = "ndbioimage"
|
||||||
version = "2025.2.1"
|
version = "2025.3.0"
|
||||||
description = "Bio image reading, metadata and some affine registration."
|
description = "Bio image reading, metadata and some affine registration."
|
||||||
authors = ["W. Pomp <w.pomp@nki.nl>"]
|
authors = [
|
||||||
license = "GPLv3"
|
{ name = "W. Pomp", email = "w.pomp@nki.nl" }
|
||||||
|
]
|
||||||
|
license = { text = "GPL-3.0-or-later"}
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
keywords = ["bioformats", "imread", "numpy", "metadata"]
|
keywords = ["bioformats", "imread", "numpy", "metadata"]
|
||||||
include = ["transform.txt"]
|
include = ["transform.txt"]
|
||||||
repository = "https://github.com/wimpomp/ndbioimage"
|
requires-python = ">=3.10"
|
||||||
exclude = ["ndbioimage/jars"]
|
exclude = ["ndbioimage/jars"]
|
||||||
|
|
||||||
[tool.poetry.dependencies]
|
dependencies = [
|
||||||
python = "^3.10"
|
"czifile == 2019.7.2",
|
||||||
numpy = ">=1.20.0"
|
"imagecodecs",
|
||||||
pandas = "*"
|
"JPype1",
|
||||||
tifffile = "*"
|
"lxml",
|
||||||
czifile = "2019.7.2"
|
"numpy >= 1.20",
|
||||||
tiffwrite = ">=2024.12.1"
|
"ome-types",
|
||||||
ome-types = ">=0.4.0"
|
"pandas",
|
||||||
pint = "*"
|
"parfor >= 2025.1.0",
|
||||||
tqdm = "*"
|
"pint",
|
||||||
lxml = "*"
|
"pyyaml",
|
||||||
pyyaml = "*"
|
"SimpleITK-SimpleElastix; sys_platform != 'darwin'",
|
||||||
parfor = ">=2025.1.0"
|
"scikit-image",
|
||||||
JPype1 = "*"
|
"tifffile <= 2025.1.10",
|
||||||
SimpleITK-SimpleElastix = [
|
"tiffwrite >= 2024.12.1",
|
||||||
{ version = "*", python = "<3.12" },
|
"tqdm",
|
||||||
{ version = "*", python = ">=3.12", markers = "sys_platform != 'darwin'" },
|
|
||||||
{ version = "*", python = ">=3.12", markers = "platform_machine == 'aarch64'" },
|
|
||||||
]
|
]
|
||||||
scikit-image = "*"
|
|
||||||
imagecodecs = "*"
|
|
||||||
xsdata = "^23" # until pydantic is up-to-date
|
|
||||||
matplotlib = { version = "*", optional = true }
|
|
||||||
scikit-video = { version = "*", optional = true }
|
|
||||||
pytest = { version = "*", optional = true }
|
|
||||||
|
|
||||||
[tool.poetry.extras]
|
[project.optional-dependencies]
|
||||||
test = ["pytest"]
|
test = ["pytest"]
|
||||||
write = ["matplotlib", "scikit-video"]
|
write = ["matplotlib", "scikit-video"]
|
||||||
|
|
||||||
[tool.poetry.scripts]
|
[project.urls]
|
||||||
|
repository = "https://github.com/wimpomp/ndbioimage"
|
||||||
|
|
||||||
|
[project.scripts]
|
||||||
ndbioimage = "ndbioimage:main"
|
ndbioimage = "ndbioimage:main"
|
||||||
|
|
||||||
[tool.pytest.ini_options]
|
[tool.pytest.ini_options]
|
||||||
|
|||||||
Reference in New Issue
Block a user