images2ims.py 7.1 KB

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