diff --git a/ndbioimage/__init__.py b/ndbioimage/__init__.py index 0d7c920..9ec0799 100755 --- a/ndbioimage/__init__.py +++ b/ndbioimage/__init__.py @@ -24,7 +24,7 @@ from pint import set_application_registry from tiffwrite import IFD, IJTiffFile # noqa from tqdm.auto import tqdm -from .jvm import JVM +from .jvm import JVM, JVMException from .transforms import Transform, Transforms try: @@ -1134,12 +1134,13 @@ class AbstractReader(Imread, metaclass=ABCMeta): try: t0 = find(image.pixels.planes, the_c=0, the_t=0, the_z=0).delta_t t1 = find(image.pixels.planes, the_c=0, the_t=self.shape['t'] - 1, the_z=0).delta_t - self.timeinterval = (t1 - t0) / (self.shape['t'] - 1) if self.shape['t'] > 1 else None + self.timeinterval = (t1 - t0) / (self.shape['t'] - 1) if self.shape['t'] > 1 and t1 > t0 else None except AttributeError: self.timeinterval = None try: self.binning = [int(i) for i in image.pixels.channels[0].detector_settings.binning.value.split('x')] - self.pxsize *= self.binning[0] + if self.pxsize is not None: + self.pxsize *= self.binning[0] except (AttributeError, IndexError, ValueError): self.binning = None self.channel_names = [channel.name for channel in image.pixels.channels] diff --git a/ndbioimage/jvm.py b/ndbioimage/jvm.py index 0230caa..777f324 100644 --- a/ndbioimage/jvm.py +++ b/ndbioimage/jvm.py @@ -1,6 +1,11 @@ from pathlib import Path from urllib import request + +class JVMException(Exception): + pass + + try: class JVM: """ There can be only one java virtual machine per python process, diff --git a/ndbioimage/readers/bfread.py b/ndbioimage/readers/bfread.py index fca41b6..bb0b0b8 100644 --- a/ndbioimage/readers/bfread.py +++ b/ndbioimage/readers/bfread.py @@ -1,30 +1,34 @@ import multiprocessing from abc import ABC from multiprocessing import queues -from traceback import print_exc +from traceback import format_exc +from pathlib import Path import numpy as np -from .. import JVM, AbstractReader +from .. import AbstractReader, JVM, JVMException -jars = {'bioformats_package.jar': - 'https://downloads.openmicroscopy.org/bio-formats/latest/artifacts/bioformats_package.jar'} +jars = {'bioformats_package.jar': 'https://downloads.openmicroscopy.org/bio-formats/latest/artifacts/' + 'bioformats_package.jar'} class JVMReader: - def __init__(self, path, series): + def __init__(self, path: Path, series: int) -> None: mp = multiprocessing.get_context('spawn') self.path = path self.series = series self.queue_in = mp.Queue() self.queue_out = mp.Queue() - self.queue_error = mp.Queue() self.done = mp.Event() self.process = mp.Process(target=self.run) self.process.start() - self.is_alive = True + status, message = self.queue_out.get() + if status == 'status' and message == 'started': + self.is_alive = True + else: + raise JVMException(message) - def close(self): + def close(self) -> None: if self.is_alive: self.done.set() while not self.queue_in.empty(): @@ -38,47 +42,53 @@ class JVMReader: self.process.close() self.is_alive = False - def frame(self, c, z, t): + def frame(self, c: int, z: int, t: int) -> np.ndarray: self.queue_in.put((c, z, t)) - return self.queue_out.get() + status, message = self.queue_out.get() + if status == 'frame': + return message + else: + raise JVMException(message) - def run(self): + def run(self) -> None: """ Read planes from the image reader file. adapted from python-bioformats/bioformats/formatreader.py """ - jvm = JVM(jars) - reader = jvm.image_reader() - ome_meta = jvm.metadata_tools.createOMEXMLMetadata() - reader.setMetadataStore(ome_meta) - reader.setId(str(self.path)) - reader.setSeries(self.series) - - open_bytes_func = reader.openBytes - width, height = int(reader.getSizeX()), int(reader.getSizeY()) - - pixel_type = reader.getPixelType() - little_endian = reader.isLittleEndian() - - if pixel_type == jvm.format_tools.INT8: - dtype = np.int8 - elif pixel_type == jvm.format_tools.UINT8: - dtype = np.uint8 - elif pixel_type == jvm.format_tools.UINT16: - dtype = 'u2' - elif pixel_type == jvm.format_tools.INT16: - dtype = 'i2' - elif pixel_type == jvm.format_tools.UINT32: - dtype = 'u4' - elif pixel_type == jvm.format_tools.INT32: - dtype = 'i4' - elif pixel_type == jvm.format_tools.FLOAT: - dtype = 'f4' - elif pixel_type == jvm.format_tools.DOUBLE: - dtype = 'f8' - else: - dtype = None - + jvm = None try: + jvm = JVM(jars) + reader = jvm.image_reader() + ome_meta = jvm.metadata_tools.createOMEXMLMetadata() + reader.setMetadataStore(ome_meta) + reader.setId(str(self.path)) + reader.setSeries(self.series) + + open_bytes_func = reader.openBytes + width, height = int(reader.getSizeX()), int(reader.getSizeY()) + + pixel_type = reader.getPixelType() + little_endian = reader.isLittleEndian() + + if pixel_type == jvm.format_tools.INT8: + dtype = np.int8 + elif pixel_type == jvm.format_tools.UINT8: + dtype = np.uint8 + elif pixel_type == jvm.format_tools.UINT16: + dtype = 'u2' + elif pixel_type == jvm.format_tools.INT16: + dtype = 'i2' + elif pixel_type == jvm.format_tools.UINT32: + dtype = 'u4' + elif pixel_type == jvm.format_tools.INT32: + dtype = 'i4' + elif pixel_type == jvm.format_tools.FLOAT: + dtype = 'f4' + elif pixel_type == jvm.format_tools.DOUBLE: + dtype = 'f8' + else: + dtype = None + self.queue_out.put(('status', 'started')) + while not self.done.is_set(): try: c, z, t = self.queue_in.get(True, 0.02) @@ -151,19 +161,19 @@ class JVMReader: image.shape = (height, width) if image.ndim == 3: - self.queue_out.put(image[..., c]) + self.queue_out.put(('frame', image[..., c])) else: - self.queue_out.put(image) + self.queue_out.put(('frame', image)) except queues.Empty: # noqa continue except (Exception,): - print_exc() - self.queue_out.put(np.zeros((32, 32))) + self.queue_out.put(('error', format_exc())) finally: - jvm.kill_vm() + if jvm is not None: + jvm.kill_vm() -def can_open(path): +def can_open(path: Path) -> bool: try: jvm = JVM(jars) reader = jvm.image_reader() @@ -183,17 +193,16 @@ class Reader(AbstractReader, ABC): do_not_pickle = 'reader', 'key', 'jvm' @staticmethod - def _can_open(path): + def _can_open(path: Path) -> bool: """ Use java BioFormats to make an ome metadata structure. """ with multiprocessing.get_context('spawn').Pool(1) as pool: - ome = pool.map(can_open, (path,))[0] - return ome + return pool.map(can_open, (path,))[0] - def open(self): + def open(self) -> None: self.reader = JVMReader(self.path, self.series) - def __frame__(self, c, z, t): + def __frame__(self, c: int, z: int, t: int) -> np.ndarray: return self.reader.frame(c, z, t) - def close(self): + def close(self) -> None: self.reader.close() diff --git a/pyproject.toml b/pyproject.toml index 6d5e1b0..8d869ec 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "ndbioimage" -version = "2024.5.0" +version = "2024.5.1" description = "Bio image reading, metadata and some affine registration." authors = ["W. Pomp "] license = "GPLv3"