PageRenderTime 49ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/scikits/image/io/collection.py

https://github.com/sccolbert/scikits.image
Python | 321 lines | 278 code | 7 blank | 36 comment | 0 complexity | 465e4e017bc3103455aa9227a614005f MD5 | raw file
  1. """Data structures to hold collections of images, with optional caching."""
  2. from __future__ import with_statement
  3. __all__ = ['MultiImage', 'ImageCollection', 'imread']
  4. from glob import glob
  5. import os.path
  6. import numpy as np
  7. from io import imread
  8. from PIL import Image
  9. class MultiImage(object):
  10. """A class containing a single multi-frame image.
  11. Parameters
  12. ----------
  13. filename : str
  14. The complete path to the image file.
  15. conserve_memory : bool, optional
  16. Whether to conserve memory by only caching a single frame. Default is
  17. True.
  18. dtype : dtype, optional
  19. NumPy data-type specifier. If given, the returned image has this type.
  20. If None (default), the data-type is determined automatically.
  21. Attributes
  22. ----------
  23. filename : str
  24. The complete path to the image file.
  25. conserve_memory : bool
  26. Whether memory is conserved by only caching a single frame.
  27. numframes : int
  28. The number of frames in the image.
  29. Notes
  30. -----
  31. If ``conserve_memory=True`` the memory footprint can be reduced, however
  32. the performance can be affected because frames have to be read from file
  33. more often.
  34. The last accessed frame is cached, all other frames will have to be read
  35. from file.
  36. Examples
  37. --------
  38. >>> from scikits.image import data_dir
  39. >>> img = MultiImage(data_dir + '/multipage.tif')
  40. >>> len(img)
  41. 2
  42. >>> for frame in img:
  43. ... print frame.shape
  44. (15, 10)
  45. (15, 10)
  46. The two frames in this image can be shown with matplotlib:
  47. .. plot:: show_collection.py
  48. """
  49. def __init__(self, filename, conserve_memory=True, dtype=None):
  50. """Load a multi-img."""
  51. self._filename = filename
  52. self._conserve_memory = conserve_memory
  53. self._dtype = dtype
  54. self._cached = None
  55. img = Image.open(self._filename)
  56. if self._conserve_memory:
  57. self._numframes = self._find_numframes(img)
  58. else:
  59. self._frames = self._getallframes(img)
  60. self._numframes = len(self._frames)
  61. @property
  62. def filename(self):
  63. return self._filename
  64. @property
  65. def conserve_memory(self):
  66. return self._conserve_memory
  67. def _find_numframes(self, img):
  68. """Find the number of frames in the multi-img."""
  69. i = 0
  70. while True:
  71. i += 1
  72. try:
  73. img.seek(i)
  74. except EOFError:
  75. break
  76. return i
  77. def _getframe(self, framenum):
  78. """Open the image and extract the frame."""
  79. img = Image.open(self.filename)
  80. img.seek(framenum)
  81. return np.asarray(img, dtype=self._dtype)
  82. def _getallframes(self, img):
  83. """Extract all frames from the multi-img."""
  84. frames = []
  85. try:
  86. i = 0
  87. while True:
  88. frames.append(np.asarray(img, dtype=self._dtype))
  89. i += 1
  90. img.seek(i)
  91. except EOFError:
  92. return frames
  93. def __getitem__(self, n):
  94. """Return the n-th frame as an array.
  95. Parameters
  96. ----------
  97. n : int
  98. Number of the required frame.
  99. Returns
  100. -------
  101. frame : ndarray
  102. The n-th frame.
  103. """
  104. numframes = self._numframes
  105. if -numframes <= n < numframes:
  106. n = n % numframes
  107. else:
  108. raise IndexError, "There are only %s frames in the image"%numframes
  109. if self.conserve_memory:
  110. if not self._cached == n:
  111. frame = self._getframe(n)
  112. self._cached = n
  113. self._cachedframe = frame
  114. return self._cachedframe
  115. else:
  116. return self._frames[n]
  117. def __iter__(self):
  118. """Iterate over the frames."""
  119. for i in range(len(self)):
  120. yield self[i]
  121. def __len__(self):
  122. """Number of images in collection."""
  123. return self._numframes
  124. def __str__(self):
  125. return str(self.filename) + ' [%s frames]'%self._numframes
  126. class ImageCollection(object):
  127. """Load and manage a collection of image files.
  128. Note that files are always stored in alphabetical order.
  129. Parameters
  130. ----------
  131. file_pattern : str or list of str
  132. Path(s) and pattern(s) of files to load. The path can be absolute
  133. or relative. If given as a list of strings, each string in the list
  134. is a separate pattern. Files are found by passing the pattern(s) to
  135. the ``glob.glob`` function.
  136. conserve_memory : bool, optional
  137. If True, never keep more than one in memory at a specific
  138. time. Otherwise, images will be cached once they are loaded.
  139. as_grey : bool, optional
  140. If True, convert the input images to grey-scale. This does not
  141. affect images that are already in a grey-scale format.
  142. dtype : dtype, optional
  143. NumPy data-type specifier. If given, the returned image has this type.
  144. If None (default), the data-type is determined automatically.
  145. Attributes
  146. ----------
  147. files : list of str
  148. A list of files in the collection, ordered alphabetically.
  149. as_grey : bool
  150. Whether images are converted to grey-scale.
  151. Examples
  152. --------
  153. >>> import scikits.image.io as io
  154. >>> from scikits.image import data_dir
  155. >>> coll = io.ImageCollection(data_dir + '/lena*.png')
  156. >>> len(coll)
  157. 2
  158. >>> coll[0].shape
  159. (128, 128, 3)
  160. When `as_grey` is set to True, a color image is returned in grey-scale:
  161. >>> coll = io.ImageCollection(data_dir + '/lena*.png', as_grey=True)
  162. >>> coll[0].shape
  163. (128, 128)
  164. """
  165. def __init__(self, file_pattern, conserve_memory=True, as_grey=False,
  166. dtype=None):
  167. """Load and manage a collection of images."""
  168. if isinstance(file_pattern, basestring):
  169. self._files = sorted(glob(file_pattern))
  170. elif isinstance(file_pattern, list):
  171. self._files = []
  172. for pattern in file_pattern:
  173. self._files.extend(glob(pattern))
  174. self._files.sort()
  175. if conserve_memory:
  176. memory_slots = 1
  177. else:
  178. memory_slots = len(self._files)
  179. self._conserve_memory = conserve_memory
  180. self._cached = None
  181. self._as_grey = as_grey
  182. self._dtype = dtype
  183. self.data = np.empty(memory_slots, dtype=object)
  184. @property
  185. def files(self):
  186. return self._files
  187. def _get_as_grey(self):
  188. """Whether images are converted to grey-scale.
  189. If this property is changed, all images in memory get reloaded.
  190. """
  191. return self._as_grey
  192. def _set_as_grey(self, newgrey):
  193. if not newgrey == self._as_grey:
  194. self._as_grey = newgrey
  195. self.reload()
  196. as_grey = property(_get_as_grey, _set_as_grey)
  197. @property
  198. def conserve_memory(self):
  199. return self._conserve_memory
  200. def __getitem__(self, n):
  201. """Return image n in the collection.
  202. Loading is done on demand.
  203. Parameters
  204. ----------
  205. n : int
  206. The image number to be returned.
  207. Returns
  208. -------
  209. img : ndarray
  210. The `n`-th image in the collection.
  211. """
  212. n = self._check_imgnum(n)
  213. idx = n % len(self.data)
  214. if (self.conserve_memory and n != self._cached) or \
  215. (self.data[idx] is None):
  216. self.data[idx] = imread(self.files[n], self.as_grey,
  217. dtype=self._dtype)
  218. self._cached = n
  219. return self.data[idx]
  220. def _check_imgnum(self, n):
  221. """Check that the given image number is valid."""
  222. num = len(self.files)
  223. if -num <= n < num:
  224. n = n % num
  225. else:
  226. raise IndexError, "There are only %s images in the collection"%num
  227. return n
  228. def __iter__(self):
  229. """Iterate over the images."""
  230. for i in range(len(self)):
  231. yield self[i]
  232. def __len__(self):
  233. """Number of images in collection."""
  234. return len(self.files)
  235. def __str__(self):
  236. return str(self.files)
  237. def reload(self, n=None):
  238. """Reload one or more images from file.
  239. Parameters
  240. ----------
  241. n : None or int
  242. The number of the image to reload. If None (default), all images in
  243. memory are reloaded. If `n` specifies an image not yet in memory,
  244. it is loaded.
  245. Returns
  246. -------
  247. None
  248. Notes
  249. -----
  250. `reload` is used to reload all images in memory when `as_grey` is
  251. changed.
  252. """
  253. if n is not None:
  254. n = self._check_numimg(n)
  255. idx = n % len(self.data)
  256. self.data[idx] = imread(self.files[n], self.as_grey,
  257. dtype=self._dtype)
  258. else:
  259. for idx, img in enumerate(self.data):
  260. if img is not None:
  261. self.data[idx] = imread(self.files[idx], self.as_grey,
  262. dtype=self._dtype)