# -*- coding: utf-8 -*-
|
|
|
|
import numpy as np
|
|
import warnings
|
|
|
|
|
|
class Image(np.ndarray):
|
|
def __new__(cls, array):
|
|
return np.asarray(array).view(cls)
|
|
|
|
def __init__(self, array):
|
|
self._timestamp = None
|
|
self._frame_number = None
|
|
self._field_of_view = None
|
|
self._channel = None
|
|
self._z_level = None
|
|
|
|
def add_params(self, timestamp, frame_number, field_of_view, channel, z_level):
|
|
"""
|
|
A wrapper around the raw pixel data of an image.
|
|
|
|
:param timestamp: The number of milliseconds after the beginning of the acquisition that this image was taken.
|
|
:type timestamp: float
|
|
:param frame_number: The order in which this image was taken, with images of different channels/z-levels
|
|
at the same field of view treated as being in the same frame.
|
|
: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: The name of the color of this image
|
|
:type channel: str
|
|
:param z_level: The label for the location in the Z-plane where this image was taken.
|
|
:type z_level: int
|
|
|
|
"""
|
|
self._timestamp = timestamp
|
|
self._frame_number = int(frame_number)
|
|
self._field_of_view = field_of_view
|
|
self._channel = channel
|
|
self._z_level = z_level
|
|
|
|
@property
|
|
def height(self):
|
|
return self.shape[1]
|
|
|
|
@property
|
|
def width(self):
|
|
return self.shape[0]
|
|
|
|
@property
|
|
def field_of_view(self):
|
|
"""
|
|
Which of the fixed locations this image was taken at.
|
|
|
|
:rtype int:
|
|
|
|
"""
|
|
return self._field_of_view
|
|
|
|
@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.
|
|
|
|
:rtype float:
|
|
|
|
"""
|
|
return self._timestamp / 1000.0
|
|
|
|
@property
|
|
def frame_number(self):
|
|
return self._frame_number
|
|
|
|
@property
|
|
def channel(self):
|
|
"""
|
|
The name of the filter used to acquire this image. These are user-supplied in NIS Elements.
|
|
|
|
:rtype str:
|
|
|
|
"""
|
|
return self._channel
|
|
|
|
@property
|
|
def z_level(self):
|
|
"""
|
|
The vertical offset of the image. These are simple integers starting from 0, where the 0 is the lowest
|
|
z-level and each subsequent level incremented by 1.
|
|
|
|
For example, if you acquired images at -3 µm, 0 µm, and +3 µm, your z-levels would be:
|
|
|
|
-3 µm: 0
|
|
0 µm: 1
|
|
+3 µm: 2
|
|
|
|
:rtype int:
|
|
|
|
"""
|
|
return self._z_level
|
|
|
|
@property
|
|
def data(self):
|
|
warnings.warn("Image objects now directly subclass Numpy arrays, so using the data attribute will be removed in the near future.", DeprecationWarning)
|
|
return self
|