Browse Source

Merge pull request #29 from jimrybarski/4-feature-image-set-storage

4 feature image set storage
zolfa-add_slices_loading
Jim Rybarski 10 years ago
parent
commit
2f5871b348
2 changed files with 53 additions and 36 deletions
  1. +20
    -26
      nd2reader/__init__.py
  2. +33
    -10
      nd2reader/model/__init__.py

+ 20
- 26
nd2reader/__init__.py View File

@ -10,7 +10,7 @@ import struct
log = logging.getLogger(__name__)
log.addHandler(logging.StreamHandler())
log.setLevel(logging.WARN)
log.setLevel(logging.DEBUG)
class Nd2(Nd2Parser):
@ -19,12 +19,6 @@ class Nd2(Nd2Parser):
self._use_image_sets = image_sets
def __iter__(self):
if self._use_image_sets:
return self.image_sets()
else:
return self.images()
def images(self):
for i in range(self._image_count):
for fov in range(self.field_of_view_count):
for z_level in range(self.z_level_count):
@ -33,6 +27,7 @@ class Nd2(Nd2Parser):
if image.is_valid:
yield image
@property
def image_sets(self):
for time_index in xrange(self.time_index_count):
image_set = ImageSet()
@ -42,7 +37,7 @@ class Nd2(Nd2Parser):
image = self.get_image(time_index, fov, channel_name, z_level)
if image.is_valid:
image_set.add(image)
yield image_set
yield image_set
def get_image(self, time_index, fov, channel_name, z_level):
image_set_number = self._calculate_image_set_number(time_index, fov, z_level)
@ -83,23 +78,22 @@ class Nd2(Nd2Parser):
@property
def absolute_start(self):
if self._absolute_start is None:
for line in self.metadata['ImageTextInfo']['SLxImageTextInfo'].values():
absolute_start_12 = None
absolute_start_24 = None
# ND2s seem to randomly switch between 12- and 24-hour representations.
try:
absolute_start_24 = datetime.strptime(line, "%m/%d/%Y %H:%M:%S")
except ValueError:
pass
try:
absolute_start_12 = datetime.strptime(line, "%m/%d/%Y %I:%M:%S %p")
except ValueError:
pass
if not absolute_start_12 and not absolute_start_24:
continue
self._absolute_start = absolute_start_12 if absolute_start_12 else absolute_start_24
return self._absolute_start
for line in self.metadata['ImageTextInfo']['SLxImageTextInfo'].values():
absolute_start_12 = None
absolute_start_24 = None
# ND2s seem to randomly switch between 12- and 24-hour representations.
try:
absolute_start_24 = datetime.strptime(line, "%m/%d/%Y %H:%M:%S")
except ValueError:
pass
try:
absolute_start_12 = datetime.strptime(line, "%m/%d/%Y %I:%M:%S %p")
except ValueError:
pass
if not absolute_start_12 and not absolute_start_24:
continue
return absolute_start_12 if absolute_start_12 else absolute_start_24
raise ValueError("This ND2 has no recorded start time. This is probably a bug.")
@property
def channel_count(self):
@ -181,4 +175,4 @@ class Nd2(Nd2Parser):
return timestamp, image_data[image_data_start::self.channel_count]
def _calculate_image_set_number(self, time_index, fov, z_level):
return time_index * self.field_of_view_count * self.z_level_count + (fov * self.z_level_count + z_level)
return time_index * self.field_of_view_count * self.z_level_count + (fov * self.z_level_count + z_level)

+ 33
- 10
nd2reader/model/__init__.py View File

@ -1,3 +1,4 @@
import collections
import numpy as np
import logging
@ -22,10 +23,10 @@ class Image(object):
@property
def timestamp(self):
"""
The number of seconds after the beginning of the acquisition that the image was taken. Note that for a given field
of view and z-level offset, if you have images of multiple channels, they will all be given the same timestamp.
No, this doesn't make much sense. But that's how ND2s are structured, so if your experiment depends on millisecond
accuracy, you need to find an alternative imaging system.
The number of seconds after the beginning of the acquisition that the image was taken. Note that for a given
field of view and z-level offset, if you have images of multiple channels, they will all be given the same
timestamp. No, this doesn't make much sense. But that's how ND2s are structured, so if your experiment depends
on millisecond accuracy, you need to find an alternative imaging system.
"""
return self._timestamp / 1000.0
@ -47,6 +48,16 @@ class Image(object):
@property
def is_valid(self):
"""
Not every image stored in an ND2 is a real image! If you take 4 images at one field of view and 2 at another
in a repeating cycle, there will be 4 images at BOTH field of view. The 2 non-images are the same size as all
the other images, only pure black (i.e. every pixel has a value of zero).
This is probably an artifact of some algorithm in NIS Elements determining the maximum number of possible
images and pre-allocating the space with zeros. Regardless of why they exit, we can't tell that they're
not actual images until we examine the data. If every pixel value is exactly 0, it's a gap image.
"""
return np.any(self._raw_data)
@ -57,15 +68,27 @@ class ImageSet(object):
"""
def __init__(self):
self._images = []
self._images = collections.defaultdict(dict)
def get(self, channel="", z_level=0):
"""
Retrieve an image with a given channel and z-level. For most users, z_level will always be 0.
"""
try:
image = self._images[channel][z_level]
except KeyError:
return None
else:
return image
def __len__(self):
""" The number of images in the image set. """
return sum([len(channel) for channel in self._images.values()])
def add(self, image):
"""
:type image: nd2reader.model.Image()
"""
self._images.append(image)
def __iter__(self):
for image in self._images:
yield image
self._images[image.channel][image.z_level] = image

Loading…
Cancel
Save