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.

139 lines
4.1 KiB

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