images2ims.py 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. # -*- coding: utf-8 -*-
  2. # Copyright (C) 2012, Almar Klein
  3. #
  4. # This code is subject to the (new) BSD license:
  5. #
  6. # Redistribution and use in source and binary forms, with or without
  7. # modification, are permitted provided that the following conditions are met:
  8. # * Redistributions of source code must retain the above copyright
  9. # notice, this list of conditions and the following disclaimer.
  10. # * Redistributions in binary form must reproduce the above copyright
  11. # notice, this list of conditions and the following disclaimer in the
  12. # documentation and/or other materials provided with the distribution.
  13. # * Neither the name of the <organization> nor the
  14. # names of its contributors may be used to endorse or promote products
  15. # derived from this software without specific prior written permission.
  16. #
  17. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  18. # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  19. # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  20. # ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
  21. # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  22. # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  23. # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  24. # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  25. # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  26. # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27. """ Module images2ims
  28. Use PIL to create a series of images.
  29. """
  30. import os
  31. try:
  32. import numpy as np
  33. except ImportError:
  34. np = None
  35. try:
  36. import PIL
  37. except ImportError:
  38. PIL = None
  39. def checkImages(images):
  40. """Check numpy images and correct intensity range etc.
  41. The same for all movie formats.
  42. :param images:
  43. """
  44. # Init results
  45. images2 = []
  46. for im in images:
  47. if PIL and isinstance(im, PIL.Image.Image):
  48. # We assume PIL images are allright
  49. images2.append(im)
  50. elif np and isinstance(im, np.ndarray):
  51. # Check and convert dtype
  52. if im.dtype == np.uint8:
  53. images2.append(im) # Ok
  54. elif im.dtype in [np.float32, np.float64]:
  55. theMax = im.max()
  56. if theMax > 128 and theMax < 300:
  57. pass # assume 0:255
  58. else:
  59. im = im.copy()
  60. im[im < 0] = 0
  61. im[im > 1] = 1
  62. im *= 255
  63. images2.append(im.astype(np.uint8))
  64. else:
  65. im = im.astype(np.uint8)
  66. images2.append(im)
  67. # Check size
  68. if im.ndim == 2:
  69. pass # ok
  70. elif im.ndim == 3:
  71. if im.shape[2] not in [3, 4]:
  72. raise ValueError('This array can not represent an image.')
  73. else:
  74. raise ValueError('This array can not represent an image.')
  75. else:
  76. raise ValueError('Invalid image type: ' + str(type(im)))
  77. # Done
  78. return images2
  79. def _getFilenameParts(filename):
  80. if '*' in filename:
  81. return tuple(filename.split('*', 1))
  82. else:
  83. return os.path.splitext(filename)
  84. def _getFilenameWithFormatter(filename, N):
  85. # Determine sequence number formatter
  86. formatter = '%04i'
  87. if N < 10:
  88. formatter = '%i'
  89. elif N < 100:
  90. formatter = '%02i'
  91. elif N < 1000:
  92. formatter = '%03i'
  93. # Insert sequence number formatter
  94. part1, part2 = _getFilenameParts(filename)
  95. return part1 + formatter + part2
  96. def _getSequenceNumber(filename, part1, part2):
  97. # Get string bit
  98. seq = filename[len(part1):-len(part2)]
  99. # Get all numeric chars
  100. seq2 = ''
  101. for c in seq:
  102. if c in '0123456789':
  103. seq2 += c
  104. else:
  105. break
  106. # Make int and return
  107. return int(seq2)
  108. def writeIms(filename, images):
  109. """Export movie to a series of image files. If the filenenumber
  110. contains an asterix, a sequence number is introduced at its
  111. location. Otherwise the sequence number is introduced right
  112. before the final dot.
  113. To enable easy creation of a new directory with image files,
  114. it is made sure that the full path exists.
  115. Images should be a list consisting of PIL images or numpy arrays.
  116. The latter should be between 0 and 255 for integer types, and
  117. between 0 and 1 for float types.
  118. :param filename:
  119. :param images:
  120. """
  121. # Check PIL
  122. if PIL is None:
  123. raise RuntimeError("Need PIL to write series of image files.")
  124. # Check images
  125. images = checkImages(images)
  126. # Get dirname and filename
  127. filename = os.path.abspath(filename)
  128. dirname, filename = os.path.split(filename)
  129. # Create dir(s) if we need to
  130. if not os.path.isdir(dirname):
  131. os.makedirs(dirname)
  132. # Insert formatter
  133. filename = _getFilenameWithFormatter(filename, len(images))
  134. # Write
  135. seq = 0
  136. for frame in images:
  137. seq += 1
  138. # Get filename
  139. fname = os.path.join(dirname, filename % seq)
  140. # Write image
  141. if np and isinstance(frame, np.ndarray):
  142. frame = PIL.Image.fromarray(frame)
  143. frame.save(fname)
  144. def readIms(filename, asNumpy=True):
  145. """ readIms(filename, asNumpy=True)
  146. Read images from a series of images in a single directory. Returns a
  147. list of numpy arrays, or, if asNumpy is false, a list if PIL images.
  148. :param filename:
  149. :param bool asNumpy:
  150. """
  151. # Check PIL
  152. if PIL is None:
  153. raise RuntimeError("Need PIL to read a series of image files.")
  154. # Check Numpy
  155. if asNumpy and np is None:
  156. raise RuntimeError("Need Numpy to return numpy arrays.")
  157. # Get dirname and filename
  158. filename = os.path.abspath(filename)
  159. dirname, filename = os.path.split(filename)
  160. # Check dir exists
  161. if not os.path.isdir(dirname):
  162. raise IOError('Directory not found: '+str(dirname))
  163. # Get two parts of the filename
  164. part1, part2 = _getFilenameParts(filename)
  165. # Init images
  166. images = []
  167. # Get all files in directory
  168. for fname in os.listdir(dirname):
  169. if fname.startswith(part1) and fname.endswith(part2):
  170. # Get sequence number
  171. nr = _getSequenceNumber(fname, part1, part2)
  172. # Get Pil image and store copy (to prevent keeping the file)
  173. im = PIL.Image.open(os.path.join(dirname, fname))
  174. images.append((im.copy(), nr))
  175. # Sort images
  176. images.sort(key=lambda x: x[1])
  177. images = [im[0] for im in images]
  178. # Convert to numpy if needed
  179. if asNumpy:
  180. images2 = images
  181. images = []
  182. for im in images2:
  183. # Make without palette
  184. if im.mode == 'P':
  185. im = im.convert()
  186. # Make numpy array
  187. a = np.asarray(im)
  188. if len(a.shape) == 0:
  189. raise MemoryError("Too little memory to convert PIL image to array")
  190. # Add
  191. images.append(a)
  192. # Done
  193. return images