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.

156 lines
5.2 KiB

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