images2ims.py 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  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. from PIL import Image
  38. except ImportError:
  39. PIL = None
  40. def checkImages(images):
  41. """ checkImages(images)
  42. Check numpy images and correct intensity range etc.
  43. The same for all movie formats.
  44. """
  45. # Init results
  46. images2 = []
  47. for im in images:
  48. if PIL and isinstance(im, PIL.Image.Image):
  49. # We assume PIL images are allright
  50. images2.append(im)
  51. elif np and isinstance(im, np.ndarray):
  52. # Check and convert dtype
  53. if im.dtype == np.uint8:
  54. images2.append(im) # Ok
  55. elif im.dtype in [np.float32, np.float64]:
  56. theMax = im.max()
  57. if theMax > 128 and theMax < 300:
  58. pass # assume 0:255
  59. else:
  60. im = im.copy()
  61. im[im<0] = 0
  62. im[im>1] = 1
  63. im *= 255
  64. images2.append( im.astype(np.uint8) )
  65. else:
  66. im = im.astype(np.uint8)
  67. images2.append(im)
  68. # Check size
  69. if im.ndim == 2:
  70. pass # ok
  71. elif im.ndim == 3:
  72. if im.shape[2] not in [3,4]:
  73. raise ValueError('This array can not represent an image.')
  74. else:
  75. raise ValueError('This array can not represent an image.')
  76. else:
  77. raise ValueError('Invalid image type: ' + str(type(im)))
  78. # Done
  79. return images2
  80. def _getFilenameParts(filename):
  81. if '*' in filename:
  82. return tuple( filename.split('*',1) )
  83. else:
  84. return os.path.splitext(filename)
  85. def _getFilenameWithFormatter(filename, N):
  86. # Determine sequence number formatter
  87. formatter = '%04i'
  88. if N < 10:
  89. formatter = '%i'
  90. elif N < 100:
  91. formatter = '%02i'
  92. elif N < 1000:
  93. formatter = '%03i'
  94. # Insert sequence number formatter
  95. part1, part2 = _getFilenameParts(filename)
  96. return part1 + formatter + part2
  97. def _getSequenceNumber(filename, part1, part2):
  98. # Get string bit
  99. seq = filename[len(part1):-len(part2)]
  100. # Get all numeric chars
  101. seq2 = ''
  102. for c in seq:
  103. if c in '0123456789':
  104. seq2 += c
  105. else:
  106. break
  107. # Make int and return
  108. return int(seq2)
  109. def writeIms(filename, images):
  110. """ writeIms(filename, images)
  111. Export movie to a series of image files. If the filenenumber
  112. contains an asterix, a sequence number is introduced at its
  113. location. Otherwise the sequence number is introduced right
  114. before the final dot.
  115. To enable easy creation of a new directory with image files,
  116. it is made sure that the full path exists.
  117. Images should be a list consisting of PIL images or numpy arrays.
  118. The latter should be between 0 and 255 for integer types, and
  119. between 0 and 1 for float types.
  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. """
  149. # Check PIL
  150. if PIL is None:
  151. raise RuntimeError("Need PIL to read a series of image files.")
  152. # Check Numpy
  153. if asNumpy and np is None:
  154. raise RuntimeError("Need Numpy to return numpy arrays.")
  155. # Get dirname and filename
  156. filename = os.path.abspath(filename)
  157. dirname, filename = os.path.split(filename)
  158. # Check dir exists
  159. if not os.path.isdir(dirname):
  160. raise IOError('Directory not found: '+str(dirname))
  161. # Get two parts of the filename
  162. part1, part2 = _getFilenameParts(filename)
  163. # Init images
  164. images = []
  165. # Get all files in directory
  166. for fname in os.listdir(dirname):
  167. if fname.startswith(part1) and fname.endswith(part2):
  168. # Get sequence number
  169. nr = _getSequenceNumber(fname, part1, part2)
  170. # Get Pil image and store copy (to prevent keeping the file)
  171. im = PIL.Image.open(os.path.join(dirname, fname))
  172. images.append((im.copy(), nr))
  173. # Sort images
  174. images.sort(key=lambda x:x[1])
  175. images = [im[0] for im in images]
  176. # Convert to numpy if needed
  177. if asNumpy:
  178. images2 = images
  179. images = []
  180. for im in images2:
  181. # Make without palette
  182. if im.mode == 'P':
  183. im = im.convert()
  184. # Make numpy array
  185. a = np.asarray(im)
  186. if len(a.shape)==0:
  187. raise MemoryError("Too little memory to convert PIL image to array")
  188. # Add
  189. images.append(a)
  190. # Done
  191. return images