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.

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