Browse Source

Resolve issue #24

feature/load_slices
Ruben Verweij 5 years ago
parent
commit
0fd78a4175
3 changed files with 21 additions and 79 deletions
  1. +4
    -4
      nd2reader/parser.py
  2. +13
    -70
      nd2reader/reader.py
  3. +4
    -5
      tests/test_reader.py

+ 4
- 4
nd2reader/parser.py View File

@ -76,7 +76,7 @@ class Parser(object):
except (TypeError): except (TypeError):
return Frame([], frame_no=frame_number, metadata=self._get_frame_metadata()) return Frame([], frame_no=frame_number, metadata=self._get_frame_metadata())
else: else:
return image
return Frame(image, frame_no=frame_number, metadata=self._get_frame_metadata())
def get_image_by_attributes(self, frame_number, field_of_view, channel, z_level, height, width): def get_image_by_attributes(self, frame_number, field_of_view, channel, z_level, height, width):
"""Gets an image based on its attributes alone """Gets an image based on its attributes alone
@ -105,7 +105,7 @@ class Parser(object):
except (TypeError): except (TypeError):
return Frame([], frame_no=frame_number, metadata=self._get_frame_metadata()) return Frame([], frame_no=frame_number, metadata=self._get_frame_metadata())
else: else:
return raw_image_data
return Frame(raw_image_data, frame_no=frame_number, metadata=self._get_frame_metadata())
@staticmethod @staticmethod
def get_dtype_from_metadata(): def get_dtype_from_metadata():
@ -283,14 +283,14 @@ class Parser(object):
# other cycle to reduce phototoxicity, but NIS Elements still allocated memory as if we were going to take # other cycle to reduce phototoxicity, but NIS Elements still allocated memory as if we were going to take
# them every cycle. # them every cycle.
if np.any(image_data): if np.any(image_data):
return timestamp, Frame(image_data, metadata=self._get_frame_metadata())
return timestamp, image_data
# If a blank "gap" image is encountered, generate an array of corresponding height and width to avoid # If a blank "gap" image is encountered, generate an array of corresponding height and width to avoid
# errors with ND2-files with missing frames. Array is filled with nan to reflect that data is missing. # errors with ND2-files with missing frames. Array is filled with nan to reflect that data is missing.
else: else:
empty_frame = np.full((height, width), np.nan) empty_frame = np.full((height, width), np.nan)
warnings.warn('ND2 file contains gap frames which are represented by np.nan-filled arrays; to convert to zeros use e.g. np.nan_to_num(array)') warnings.warn('ND2 file contains gap frames which are represented by np.nan-filled arrays; to convert to zeros use e.g. np.nan_to_num(array)')
return timestamp, Frame(empty_frame, metadata=self._get_frame_metadata())
return timestamp, image_data
def _get_frame_metadata(self): def _get_frame_metadata(self):
"""Get the metadata for one frame """Get the metadata for one frame


+ 13
- 70
nd2reader/reader.py View File

@ -54,70 +54,22 @@ class ND2Reader(FramesSequenceND):
return 0 return 0
def get_frame_2D(self, c=0, t=0, z=0, x=0, y=0, v=0): def get_frame_2D(self, c=0, t=0, z=0, x=0, y=0, v=0):
"""Fallback function for backwards compatibility
"""
return self.get_frame_vczyx(v=v, c=c, t=t, z=z, x=x, y=y)
def get_frame_vczyx(self, v=None, c=None, t=None, z=None, x=None, y=None):
"""Retrieve a frame based on the specified coordinates
Axes order is set by self.bundle_axes, x and y coordinates are ignored,
because we always return Frame objects.
"""Gets a given frame using the parser
Args:
x: The x-index (pims expects this)
y: The y-index (pims expects this)
c: The color channel number
t: The frame number
z: The z stack number
v: The field of view index
Returns:
pims.Frame: The requested frame
""" """
# remove 'x', 'y' from bundle axes and set to width, height
bundle_axes = list(self.bundle_axes)
try:
bundle_axes.remove('x')
except ValueError:
pass
try:
bundle_axes.remove('y')
except ValueError:
pass
# This needs to be set to width/height to return an image
x = self.metadata["width"] x = self.metadata["width"]
y = self.metadata["height"] y = self.metadata["height"]
# make coords dictionary based on function input
coords = dict(v=v, c=c, t=t, z=z)
# Set appropriate values for None and bundle_axes coords
for dim in coords:
coords[dim] = self._get_possible_coords(dim, coords[dim])
# Initialize empty array of Frames of right shape
if len(bundle_axes) > 0:
shape = tuple((len(coords[dim]) for dim in bundle_axes))
results = np.empty(shape, dtype=Frame)
else:
results = np.empty((1,), dtype=Frame)
# order for the get_image_by_attributes function
argument_order = dict(t=0, v=1, c=2, z=3)
# Now, collect the results in the right order
for index, _ in np.ndenumerate(results):
current_coords = [0, 0, 0, 0, y, x]
for dim in coords:
if dim in bundle_axes:
dim_val = coords[dim][index[bundle_axes.index(dim)]]
else:
dim_val = coords[dim][0]
current_coords[argument_order[dim]] = dim_val
# Actually get the corresponding Frame
results[index] = Frame(self._parser.get_image_by_attributes(*current_coords), metadata=self.metadata)
if len(bundle_axes) == 0:
return results[0]
return results
def _get_possible_coords(self, dim, default):
if dim in self.sizes:
if dim in self.bundle_axes:
return range(self.sizes[dim])
else:
return [default] if default is not None else range(self.sizes[dim])
return [None]
return self._parser.get_image_by_attributes(t, v, c, z, y, x)
@property @property
def parser(self): def parser(self):
@ -206,16 +158,7 @@ class ND2Reader(FramesSequenceND):
# provide the default # provide the default
self.iter_axes = self._guess_default_iter_axis() self.iter_axes = self._guess_default_iter_axis()
self._register_get_frame(self.get_frame_vczyx, 'vczyx')
self._register_get_frame(self.get_frame_vczyx, 'vzyx')
self._register_get_frame(self.get_frame_vczyx, 'vcyx')
self._register_get_frame(self.get_frame_vczyx, 'vyx')
self._register_get_frame(self.get_frame_vczyx, 'czyx')
self._register_get_frame(self.get_frame_vczyx, 'cyx')
self._register_get_frame(self.get_frame_vczyx, 'zyx')
self._register_get_frame(self.get_frame_vczyx, 'yx')
self._register_get_frame(self.get_frame_2D, 'yx')
def _init_axis_if_exists(self, axis, size, min_size=1): def _init_axis_if_exists(self, axis, size, min_size=1):
if size >= min_size: if size >= min_size:


+ 4
- 5
tests/test_reader.py View File

@ -44,25 +44,24 @@ class TestReader(unittest.TestCase):
timesteps = reader.timesteps timesteps = reader.timesteps
self.assertEquals(len(timesteps), 0) self.assertEquals(len(timesteps), 0)
def test_get_frame_2D(self):
def test_get_frame_zero(self):
# Best test we can do for now: # Best test we can do for now:
# test everything up to the actual unpacking of the frame data # test everything up to the actual unpacking of the frame data
with ArtificialND2('test_data/test_nd2_reader.nd2') as _: with ArtificialND2('test_data/test_nd2_reader.nd2') as _:
with ND2Reader('test_data/test_nd2_reader.nd2') as reader: with ND2Reader('test_data/test_nd2_reader.nd2') as reader:
with self.assertRaises(struct.error) as exception: with self.assertRaises(struct.error) as exception:
frame = reader.get_frame_2D(c=0, t=0, z=0, x=0, y=0, v=0)
frame = reader[0]
self.assertIn('unpack', str(exception.exception)) self.assertIn('unpack', str(exception.exception))
def test_get_frame_vczyx(self):
def test_get_frame_2D(self):
# Best test we can do for now: # Best test we can do for now:
# test everything up to the actual unpacking of the frame data # test everything up to the actual unpacking of the frame data
with ArtificialND2('test_data/test_nd2_reader.nd2') as _: with ArtificialND2('test_data/test_nd2_reader.nd2') as _:
with ND2Reader('test_data/test_nd2_reader.nd2') as reader: with ND2Reader('test_data/test_nd2_reader.nd2') as reader:
with self.assertRaises(struct.error) as exception: with self.assertRaises(struct.error) as exception:
frame = reader.get_frame_vczyx(c=0, t=0, z=0, x=0, y=0, v=0)
frame = reader.get_frame_2D(c=0, t=0, z=0, x=0, y=0, v=0)
self.assertIn('unpack', str(exception.exception)) self.assertIn('unpack', str(exception.exception))

Loading…
Cancel
Save