You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

133 lines
3.6 KiB

8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
  1. from pims import FramesSequenceND, Frame
  2. from nd2reader.parser import Parser
  3. import numpy as np
  4. class ND2Reader(FramesSequenceND):
  5. """PIMS wrapper for the ND2 parser
  6. """
  7. def __init__(self, filename):
  8. self.filename = filename
  9. # first use the parser to parse the file
  10. self._fh = open(filename, "rb")
  11. self._parser = Parser(self._fh)
  12. # Setup metadata
  13. self.metadata = self._parser.metadata
  14. # Set data type
  15. self._dtype = self._parser.get_dtype_from_metadata()
  16. # Setup the axes
  17. self._setup_axes()
  18. @classmethod
  19. def class_exts(cls):
  20. """Let PIMS open function use this reader for opening .nd2 files
  21. """
  22. return {'nd2'} | super(ND2Reader, cls).class_exts()
  23. def close(self):
  24. """Correctly close the file handle
  25. """
  26. if self._fh is not None:
  27. self._fh.close()
  28. def get_frame(self, i):
  29. """Return one frame
  30. Args:
  31. i: The frame number
  32. Returns:
  33. numpy.ndarray: The requested frame
  34. """
  35. fetch_all_channels = 'c' in self.bundle_axes
  36. if fetch_all_channels:
  37. return self._get_frame_all_channels(i)
  38. else:
  39. return self.get_frame_2D(self.default_coords['c'], i, self.default_coords['z'])
  40. def _get_frame_all_channels(self, i):
  41. """Get all color channels for this frame
  42. Args:
  43. i: The frame number
  44. Returns:
  45. numpy.ndarray: The requested frame, with all color channels.
  46. """
  47. frames = None
  48. for c in range(len(self.metadata["channels"])):
  49. frame = self.get_frame_2D(c, i, self.default_coords['z'])
  50. if frames is None:
  51. frames = Frame([frame])
  52. else:
  53. frames = np.concatenate((frames, [frame]), axis=0)
  54. return frames
  55. def get_frame_2D(self, c, t, z):
  56. """Gets a given frame using the parser
  57. Args:
  58. c: The color channel number
  59. t: The frame number
  60. z: The z stack number
  61. Returns:
  62. numpy.ndarray: The requested frame
  63. """
  64. c_name = self.metadata["channels"][c]
  65. return self._parser.get_image_by_attributes(t, 0, c_name, z, self.metadata["height"], self.metadata["width"])
  66. @property
  67. def pixel_type(self):
  68. """Return the pixel data type
  69. Returns:
  70. dtype: the pixel data type
  71. """
  72. return self._dtype
  73. def _setup_axes(self):
  74. """Setup the xyctz axes, iterate over t axis by default
  75. """
  76. self._init_axis('x', self.metadata["width"])
  77. self._init_axis('y', self.metadata["height"])
  78. self._init_axis('c', len(self.metadata["channels"]))
  79. self._init_axis('t', len(self.metadata["frames"]))
  80. self._init_axis('z', len(self.metadata["z_levels"]))
  81. # provide the default
  82. self.iter_axes = 't'
  83. def get_timesteps(self):
  84. """Get the timesteps of the experiment
  85. Returns:
  86. np.ndarray: an array of times in milliseconds.
  87. """
  88. timesteps = np.array([])
  89. current_time = 0.0
  90. for loop in self.metadata['experiment']['loops']:
  91. if loop['stimulation']:
  92. continue
  93. timesteps = np.concatenate(
  94. (timesteps, np.arange(current_time, current_time + loop['duration'], loop['sampling_interval'])))
  95. current_time += loop['duration']
  96. # if experiment did not finish, number of timesteps is wrong. Take correct amount of leading timesteps.
  97. return timesteps[:self.metadata['num_frames']]