Browse Source

resolves #81: images now include the frame number (formerly called the time index). This tells you what order a particular image appeared in its field of view, which is useful when you're considering fields of view as independent regions of interest. Also fixed a bug where the number of image cycles (which should be renamed to "frames") was incorrect for some nd2s.

master
jim 9 years ago
parent
commit
55a3222e4f
3 changed files with 23 additions and 12 deletions
  1. +6
    -6
      nd2reader/__init__.py
  2. +10
    -2
      nd2reader/model/__init__.py
  3. +7
    -4
      nd2reader/parser.py

+ 6
- 6
nd2reader/__init__.py View File

@ -58,8 +58,9 @@ class Nd2(Nd2Parser):
channel = self._calculate_channel(item)
z_level = self._calculate_z_level(item)
image_group_number = int(item / len(self.channels))
frame_number = self._calculate_frame_number(image_group_number, fov, z_level)
timestamp, raw_image_data = self._get_raw_image_data(image_group_number, channel_offset)
image = Image(timestamp, raw_image_data, fov, channel, z_level, self.height, self.width)
image = Image(timestamp, frame_number, raw_image_data, fov, channel, z_level, self.height, self.width)
except (TypeError, ValueError):
return None
except KeyError:
@ -126,13 +127,12 @@ class Nd2(Nd2Parser):
"""
return self.metadata[six.b('ImageAttributes')][six.b('SLxImageAttributes')][six.b('uiWidth')]
def get_image(self, time_index, field_of_view, channel_name, z_level):
def get_image(self, frame_number, field_of_view, channel_name, z_level):
"""
Returns an Image if data exists for the given parameters, otherwise returns None. In general, you should avoid
using this method unless you're very familiar with the structure of ND2 files.
:param time_index: the frame number
:type time_index: int
:type frame_number: int
:param field_of_view: the label for the place in the XY-plane where this image was taken.
:type field_of_view: int
:param channel_name: the name of the color of this image
@ -142,10 +142,10 @@ class Nd2(Nd2Parser):
:rtype: nd2reader.model.Image() or None
"""
image_group_number = self._calculate_image_group_number(time_index, field_of_view, z_level)
image_group_number = self._calculate_image_group_number(frame_number, field_of_view, z_level)
try:
timestamp, raw_image_data = self._get_raw_image_data(image_group_number, self._channel_offset[channel_name])
image = Image(timestamp, raw_image_data, field_of_view, channel_name, z_level, self.height, self.width)
image = Image(timestamp, frame_number, raw_image_data, field_of_view, channel_name, z_level, self.height, self.width)
except TypeError:
return None
else:


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

@ -8,10 +8,12 @@ log = logging.getLogger(__name__)
class Image(object):
def __init__(self, timestamp, raw_array, field_of_view, channel, z_level, height, width):
def __init__(self, timestamp, frame_number, raw_array, field_of_view, channel, z_level, height, width):
"""
A wrapper around the raw pixel data of an image.
:param timestamp: The frame number relative to the .
:type timestamp: int
:param timestamp: The number of milliseconds after the beginning of the acquisition that this image was taken.
:type timestamp: int
:param raw_array: The raw sequence of bytes that represents the image.
@ -29,6 +31,7 @@ class Image(object):
"""
self._timestamp = timestamp
self._frame_number = int(frame_number)
self._raw_data = raw_array
self._field_of_view = field_of_view
self._channel = channel
@ -41,6 +44,7 @@ class Image(object):
return "\n".join(["<ND2 Image>",
"%sx%s (HxW)" % (self._height, self._width),
"Timestamp: %s" % self.timestamp,
"Frame: %s" % self._frame_number,
"Field of View: %s" % self.field_of_view,
"Channel: %s" % self.channel,
"Z-Level: %s" % self.z_level,
@ -83,6 +87,10 @@ class Image(object):
"""
return self._timestamp / 1000.0
@property
def frame_number(self):
return self._frame_number
@property
def channel(self):
"""
@ -144,4 +152,4 @@ class ImageSet(object):
:type image: nd2reader.model.Image()
"""
self._images[image.channel][image.z_level] = image
self._images[image.channel][image.z_level] = image

+ 7
- 4
nd2reader/parser.py View File

@ -69,7 +69,7 @@ class Nd2Parser(object):
These are labels created by the NIS Elements user. Typically they may a short description of the filter cube
used (e.g. "bright field", "GFP", etc.)
:rtype: str
:rtype: list
"""
if not self._channels:
@ -97,7 +97,7 @@ class Nd2Parser(object):
in the image data, so we have to calculate it. There probably is something somewhere, since
NIS Elements can figure it out, but we haven't found it yet.
:rtype: int
:rtype: list
"""
if self._fields_of_view is None:
@ -109,7 +109,7 @@ class Nd2Parser(object):
"""
The number of cycles.
:rtype: int
:rtype: list
"""
if self._time_indexes is None:
@ -121,7 +121,7 @@ class Nd2Parser(object):
"""
The different levels in the Z-plane. Just a sequence from 0 to n.
:rtype: int
:rtype: list
"""
if self._z_levels is None:
@ -214,6 +214,9 @@ class Nd2Parser(object):
"""
return time_index * len(self.fields_of_view) * len(self.z_levels) + (fov * len(self.z_levels) + z_level)
def _calculate_frame_number(self, image_group_number, fov, z_level):
return (image_group_number - (fov * len(self.z_levels) + z_level)) / (len(self.fields_of_view) * len(self.z_levels))
@property
def _channel_offset(self):
"""


Loading…
Cancel
Save