- catch 0 timeinterval

- better error handling in JVM
This commit is contained in:
Wim Pomp
2024-05-30 11:02:38 +02:00
parent ff3a43d3c9
commit e257ec326d
4 changed files with 74 additions and 59 deletions

View File

@@ -24,7 +24,7 @@ from pint import set_application_registry
from tiffwrite import IFD, IJTiffFile # noqa from tiffwrite import IFD, IJTiffFile # noqa
from tqdm.auto import tqdm from tqdm.auto import tqdm
from .jvm import JVM from .jvm import JVM, JVMException
from .transforms import Transform, Transforms from .transforms import Transform, Transforms
try: try:
@@ -1134,12 +1134,13 @@ class AbstractReader(Imread, metaclass=ABCMeta):
try: try:
t0 = find(image.pixels.planes, the_c=0, the_t=0, the_z=0).delta_t 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 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: except AttributeError:
self.timeinterval = None self.timeinterval = None
try: try:
self.binning = [int(i) for i in image.pixels.channels[0].detector_settings.binning.value.split('x')] 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): except (AttributeError, IndexError, ValueError):
self.binning = None self.binning = None
self.channel_names = [channel.name for channel in image.pixels.channels] self.channel_names = [channel.name for channel in image.pixels.channels]

View File

@@ -1,6 +1,11 @@
from pathlib import Path from pathlib import Path
from urllib import request from urllib import request
class JVMException(Exception):
pass
try: try:
class JVM: class JVM:
""" There can be only one java virtual machine per python process, """ There can be only one java virtual machine per python process,

View File

@@ -1,30 +1,34 @@
import multiprocessing import multiprocessing
from abc import ABC from abc import ABC
from multiprocessing import queues from multiprocessing import queues
from traceback import print_exc from traceback import format_exc
from pathlib import Path
import numpy as np import numpy as np
from .. import JVM, AbstractReader from .. import AbstractReader, JVM, JVMException
jars = {'bioformats_package.jar': jars = {'bioformats_package.jar': 'https://downloads.openmicroscopy.org/bio-formats/latest/artifacts/'
'https://downloads.openmicroscopy.org/bio-formats/latest/artifacts/bioformats_package.jar'} 'bioformats_package.jar'}
class JVMReader: class JVMReader:
def __init__(self, path, series): def __init__(self, path: Path, series: int) -> None:
mp = multiprocessing.get_context('spawn') mp = multiprocessing.get_context('spawn')
self.path = path self.path = path
self.series = series self.series = series
self.queue_in = mp.Queue() self.queue_in = mp.Queue()
self.queue_out = mp.Queue() self.queue_out = mp.Queue()
self.queue_error = mp.Queue()
self.done = mp.Event() self.done = mp.Event()
self.process = mp.Process(target=self.run) self.process = mp.Process(target=self.run)
self.process.start() 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: if self.is_alive:
self.done.set() self.done.set()
while not self.queue_in.empty(): while not self.queue_in.empty():
@@ -38,47 +42,53 @@ class JVMReader:
self.process.close() self.process.close()
self.is_alive = False 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)) 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. """ Read planes from the image reader file.
adapted from python-bioformats/bioformats/formatreader.py adapted from python-bioformats/bioformats/formatreader.py
""" """
jvm = JVM(jars) jvm = None
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' if little_endian else '>u2'
elif pixel_type == jvm.format_tools.INT16:
dtype = '<i2' if little_endian else '>i2'
elif pixel_type == jvm.format_tools.UINT32:
dtype = '<u4' if little_endian else '>u4'
elif pixel_type == jvm.format_tools.INT32:
dtype = '<i4' if little_endian else '>i4'
elif pixel_type == jvm.format_tools.FLOAT:
dtype = '<f4' if little_endian else '>f4'
elif pixel_type == jvm.format_tools.DOUBLE:
dtype = '<f8' if little_endian else '>f8'
else:
dtype = None
try: 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' if little_endian else '>u2'
elif pixel_type == jvm.format_tools.INT16:
dtype = '<i2' if little_endian else '>i2'
elif pixel_type == jvm.format_tools.UINT32:
dtype = '<u4' if little_endian else '>u4'
elif pixel_type == jvm.format_tools.INT32:
dtype = '<i4' if little_endian else '>i4'
elif pixel_type == jvm.format_tools.FLOAT:
dtype = '<f4' if little_endian else '>f4'
elif pixel_type == jvm.format_tools.DOUBLE:
dtype = '<f8' if little_endian else '>f8'
else:
dtype = None
self.queue_out.put(('status', 'started'))
while not self.done.is_set(): while not self.done.is_set():
try: try:
c, z, t = self.queue_in.get(True, 0.02) c, z, t = self.queue_in.get(True, 0.02)
@@ -151,19 +161,19 @@ class JVMReader:
image.shape = (height, width) image.shape = (height, width)
if image.ndim == 3: if image.ndim == 3:
self.queue_out.put(image[..., c]) self.queue_out.put(('frame', image[..., c]))
else: else:
self.queue_out.put(image) self.queue_out.put(('frame', image))
except queues.Empty: # noqa except queues.Empty: # noqa
continue continue
except (Exception,): except (Exception,):
print_exc() self.queue_out.put(('error', format_exc()))
self.queue_out.put(np.zeros((32, 32)))
finally: finally:
jvm.kill_vm() if jvm is not None:
jvm.kill_vm()
def can_open(path): def can_open(path: Path) -> bool:
try: try:
jvm = JVM(jars) jvm = JVM(jars)
reader = jvm.image_reader() reader = jvm.image_reader()
@@ -183,17 +193,16 @@ class Reader(AbstractReader, ABC):
do_not_pickle = 'reader', 'key', 'jvm' do_not_pickle = 'reader', 'key', 'jvm'
@staticmethod @staticmethod
def _can_open(path): def _can_open(path: Path) -> bool:
""" Use java BioFormats to make an ome metadata structure. """ """ Use java BioFormats to make an ome metadata structure. """
with multiprocessing.get_context('spawn').Pool(1) as pool: with multiprocessing.get_context('spawn').Pool(1) as pool:
ome = pool.map(can_open, (path,))[0] return pool.map(can_open, (path,))[0]
return ome
def open(self): def open(self) -> None:
self.reader = JVMReader(self.path, self.series) 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) return self.reader.frame(c, z, t)
def close(self): def close(self) -> None:
self.reader.close() self.reader.close()

View File

@@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "ndbioimage" name = "ndbioimage"
version = "2024.5.0" version = "2024.5.1"
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 = ["W. Pomp <w.pomp@nki.nl>"]
license = "GPLv3" license = "GPLv3"