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.

165 lines
5.3 KiB

9 years ago
9 years ago
  1. # -*- coding: utf-8 -*-
  2. from nd2reader.model import ImageGroup
  3. from nd2reader.parser import get_parser
  4. from nd2reader.version import get_version
  5. import warnings
  6. class Nd2(object):
  7. """
  8. Allows easy access to NIS Elements .nd2 image files.
  9. """
  10. def __init__(self, filename):
  11. self._filename = filename
  12. self._fh = open(filename, "rb")
  13. major_version, minor_version = get_version(self._fh)
  14. parser = get_parser(self._fh, major_version, minor_version)
  15. self._driver = parser.driver
  16. self._metadata = parser.metadata
  17. def __enter__(self):
  18. return self
  19. def __exit__(self, exc_type, exc_val, exc_tb):
  20. if self._fh is not None:
  21. self._fh.close()
  22. def __repr__(self):
  23. return "\n".join(["<ND2 %s>" % self._filename,
  24. "Created: %s" % (self.date if self.date is not None else "Unknown"),
  25. "Image size: %sx%s (HxW)" % (self.height, self.width),
  26. "Frames: %s" % len(self.frames),
  27. "Channels: %s" % ", ".join(["'%s'" % str(channel) for channel in self.channels]),
  28. "Fields of View: %s" % len(self.fields_of_view),
  29. "Z-Levels: %s" % len(self.z_levels)
  30. ])
  31. def __len__(self):
  32. """
  33. This should be the total number of images in the ND2, but it may be inaccurate. If the ND2 contains a
  34. different number of images in a cycle (i.e. there are "gap" images) it will be higher than reality.
  35. :rtype: int
  36. """
  37. return self._metadata.total_images_per_channel * len(self.channels)
  38. def __getitem__(self, item):
  39. """
  40. Allows slicing ND2s.
  41. :type item: int or slice
  42. :rtype: nd2reader.model.Image() or generator
  43. """
  44. if isinstance(item, int):
  45. try:
  46. image = self._driver.get_image(item)
  47. except KeyError:
  48. raise IndexError
  49. else:
  50. return image
  51. elif isinstance(item, slice):
  52. return self._slice(item.start, item.stop, item.step)
  53. raise IndexError
  54. def _slice(self, start, stop, step):
  55. """
  56. Allows for iteration over a selection of the entire dataset.
  57. :type start: int
  58. :type stop: int
  59. :type step: int
  60. :rtype: nd2reader.model.Image()
  61. """
  62. start = start if start is not None else 0
  63. step = step if step is not None else 1
  64. stop = stop if stop is not None else len(self)
  65. # This weird thing with the step allows you to iterate backwards over the images
  66. for i in range(start, stop)[::step]:
  67. yield self[i]
  68. @property
  69. def image_sets(self):
  70. """
  71. Iterates over groups of related images. This is useful if your ND2 contains multiple fields of view.
  72. A typical use case might be that you have, say, four areas of interest that you're monitoring, and every
  73. minute you take a bright field and GFP image of each one. For each cycle, this method would produce four
  74. ImageSet objects, each containing one bright field and one GFP image.
  75. :return: model.ImageSet()
  76. """
  77. warnings.warn("Nd2.image_sets will be removed from the nd2reader library in the near future.",
  78. DeprecationWarning)
  79. for frame in self.frames:
  80. image_group = ImageGroup()
  81. for fov in self.fields_of_view:
  82. for channel_name in self.channels:
  83. for z_level in self.z_levels:
  84. image = self.get_image(frame, fov, channel_name, z_level)
  85. if image is not None:
  86. image_group.add(image)
  87. yield image_group
  88. @property
  89. def date(self):
  90. return self._metadata.date
  91. @property
  92. def z_levels(self):
  93. return self._metadata.z_levels
  94. @property
  95. def fields_of_view(self):
  96. return self._metadata.fields_of_view
  97. @property
  98. def channels(self):
  99. return self._metadata.channels
  100. @property
  101. def frames(self):
  102. return self._metadata.frames
  103. @property
  104. def height(self):
  105. """
  106. :return: height of each image, in pixels
  107. :rtype: int
  108. """
  109. return self._metadata.height
  110. @property
  111. def width(self):
  112. """
  113. :return: width of each image, in pixels
  114. :rtype: int
  115. """
  116. return self._metadata.width
  117. def get_image(self, frame_number, field_of_view, channel_name, z_level):
  118. """
  119. Returns an Image if data exists for the given parameters, otherwise returns None.
  120. :type frame_number: int
  121. :param field_of_view: the label for the place in the XY-plane where this image was taken.
  122. :type field_of_view: int
  123. :param channel_name: the name of the color of this image
  124. :type channel_name: str
  125. :param z_level: the label for the location in the Z-plane where this image was taken.
  126. :type z_level: int
  127. :rtype: nd2reader.model.Image()
  128. """
  129. return self._driver.get_image_by_attributes(frame_number, field_of_view, channel_name, z_level, self.height, self.width)
  130. def close(self):
  131. self._fh.close()