Multidimensional Readers

Multidimensional files are files that have for instance an extra z-axis (making the image 3D, an extra channel axis, or a multipoint axis. PIMS provides a flexible and uniform interface for working with these kind of files through the base class FramesSequenceND. The readers ImageSequenceND and Bioformats (see ImageSequence or Bioformats). are based on this baseclass and provide the axis-aware methods describe below.

To avoid ambiguity, we call a point along one axis a coordinate. The word index refers to the working of frames[index]. The index is equal to the coordinate along the iterating axis (normally, t), except when the iterating axis is nested (see below).

The names and sizes of each axis is provided with the sizes property:

In [1]: images.sizes
Out[1]: {'t': 4, 'z': 10, 'c': 2, 'y': 64, 'x': 128}

Axes bundling

The bundle_axes property defines which axes will be present in a single frame. The frame_shape property is changed accordingly:

In [2]: images.bundle_axes = 'czyx'

In [3]: images.frame_shape
Out[3]: (2, 10, 64, 128)

In [4]: images.bundle_axes = 'yx'

In [5]: images.frame_shape
Out[5]: (64, 128)

Currently, the last two axes have to be 'yx'. For multi-symbol axis names, provide a list like this: images.bundle_axes = ['one', 'two'].

Axes iteration

The iter_axes property defines which axis will be used as the index axis. The reader length is updated accordingly:

In [6]: images.iter_axes = 't'

In [7]: len(images)
Out[7]: 4

When multiple axes are provided to iter_axes, a nested iteration will be performed in which the last element will iterate fastest:

In [8]: images.iter_axes = 'tz'

In [9]: len(images)  # 4 * 10
Out[9]: 40

In [10]: images[12];  # returns the image at t == 1 and z = 2

Default coordinates

What if an axis is not present in bundle_axes and iter_axes? Then the default coordinate is returned, as defined in the dictionary default_coords:

In [11]: images.bundle_axes = 'zyx'

In [12]: images.iter_axes = 't'

In [13]: images.default_coords['c'] = 1

In [14]: images[2];  # returns the 3D image at t == 2 and c = 1

Make your own multidimensional reader

Making a multidimensional reader class yourself is simple. The following example is already a fully-functioning multidimensional reader. The crucial method here is _register_get_frame, that registers a get_frame method and tells the reader which axes to expect from that method. You can also define multiple get_frame methods to increase the reader performance.

The reader then figures out how to efficiently use this function, to present the image in the shape that corresponds with the bundle_axes settings.

from pims import FramesSequenceND
import numpy as np

class IndexReturningReader(FramesSequenceND):
   @property
   def pixel_type(self):
       return np.uint8  # the pixel datatype

   def __init__(self, size_c, size_t, size_z):
       # first call the baseclass initialization
       super(IndexReturningReader, self).__init__()
       self._init_axis('x', 3)
       self._init_axis('y', 1)
       self._init_axis('c', size_c)
       self._init_axis('t', size_t)
       self._init_axis('z', size_z)
       # register the get_frame function
       self._register_get_frame(self.get_frame_func, 'yx')

   def get_frame_func(self, c, t, z):
       return np.array([[c, t, z]], dtype=np.uint8)