images2avi.py 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. # Copyright (C) 2012, Almar Klein
  2. # All rights reserved.
  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. #
  28. #
  29. # changes of this file GRASS (PNG instead of JPG) by Anna Petrasova 2013
  30. """ Module images2avi
  31. Uses ffmpeg to read and write AVI files. Requires PIL
  32. I found these sites useful:
  33. http://www.catswhocode.com/blog/19-ffmpeg-commands-for-all-needs
  34. http://linux.die.net/man/1/ffmpeg
  35. """
  36. import os
  37. import time
  38. import subprocess
  39. import shutil
  40. from grass.imaging import images2ims
  41. import grass.script as gscript
  42. def _cleanDir(tempDir):
  43. for i in range(3):
  44. try:
  45. shutil.rmtree(tempDir)
  46. except Exception:
  47. time.sleep(0.2) # Give OS time to free sources
  48. else:
  49. break
  50. else:
  51. print("Oops, could not fully clean up temporary files.")
  52. def writeAvi(filename, images, duration=0.1, encoding='mpeg4',
  53. inputOptions='', outputOptions='', bg_task=False):
  54. """Export movie to a AVI file, which is encoded with the given
  55. encoding. Hint for Windows users: the 'msmpeg4v2' codec is
  56. natively supported on Windows.
  57. Images should be a list consisting of PIL images or numpy arrays.
  58. The latter should be between 0 and 255 for integer types, and
  59. between 0 and 1 for float types.
  60. Requires the "ffmpeg" application:
  61. * Most linux users can install using their package manager
  62. * There is a windows installer on the visvis website
  63. :param str filename: output filename
  64. :param images:
  65. :param float duration:
  66. :param str encoding: the encoding type
  67. :param inputOptions:
  68. :param outputOptions:
  69. :param bool bg_task: if thread background task, not raise but
  70. return error message
  71. :return str: error message
  72. """
  73. # Get fps
  74. try:
  75. fps = float(1.0 / duration)
  76. except Exception:
  77. raise ValueError(_('Invalid duration parameter for writeAvi.'))
  78. # Determine temp dir and create images
  79. tempDir = os.path.join(os.path.expanduser('~'), '.tempIms')
  80. images2ims.writeIms(os.path.join(tempDir, 'im*.png'), images)
  81. # Determine formatter
  82. N = len(images)
  83. formatter = '%04d'
  84. if N < 10:
  85. formatter = '%d'
  86. elif N < 100:
  87. formatter = '%02d'
  88. elif N < 1000:
  89. formatter = '%03d'
  90. # Compile command to create avi
  91. command = "ffmpeg -r %i %s " % (int(fps), inputOptions)
  92. command += "-i im%s.png " % (formatter,)
  93. command += "-g 1 -vcodec %s %s " % (encoding, outputOptions)
  94. command += "output.avi"
  95. # Run ffmpeg
  96. S = subprocess.Popen(command, shell=True, cwd=tempDir,
  97. stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  98. # Show what ffmpeg has to say
  99. outPut = S.stdout.read()
  100. if S.wait():
  101. # Clean up
  102. _cleanDir(tempDir)
  103. if bg_task:
  104. return gscript.decode(outPut) + '\n' + gscript.decode(
  105. S.stderr.read()) + '\n' + _('Could not write avi.')
  106. else:
  107. # An error occurred, show
  108. print(gscript.decode(outPut))
  109. print(gscript.decode(S.stderr.read()))
  110. raise RuntimeError(_('Could not write avi.'))
  111. else:
  112. try:
  113. # Copy avi
  114. shutil.copy(os.path.join(tempDir, 'output.avi'), filename)
  115. except Exception as err:
  116. # Clean up
  117. _cleanDir(tempDir)
  118. if bg_task:
  119. return str(err)
  120. else:
  121. raise
  122. # Clean up
  123. _cleanDir(tempDir)
  124. def readAvi(filename, asNumpy=True):
  125. """Read images from an AVI (or MPG) movie.
  126. Requires the "ffmpeg" application:
  127. * Most linux users can install using their package manager
  128. * There is a windows installer on the visvis website
  129. :param str filename: name of input movie file
  130. :param bool asNumpy:
  131. """
  132. # Check whether it exists
  133. if not os.path.isfile(filename):
  134. raise IOError('File not found: '+str(filename))
  135. # Determine temp dir, make sure it exists
  136. tempDir = os.path.join(os.path.expanduser('~'), '.tempIms')
  137. if not os.path.isdir(tempDir):
  138. os.makedirs(tempDir)
  139. # Copy movie there
  140. shutil.copy(filename, os.path.join(tempDir, 'input.avi'))
  141. # Run ffmpeg
  142. command = "ffmpeg -i input.avi im%d.jpg"
  143. S = subprocess.Popen(command, shell=True, cwd=tempDir,
  144. stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  145. # Show what mencodec has to say
  146. outPut = S.stdout.read()
  147. if S.wait():
  148. # An error occurred, show
  149. print(outPut)
  150. print(S.stderr.read())
  151. # Clean up
  152. _cleanDir(tempDir)
  153. raise RuntimeError("Could not read avi.")
  154. else:
  155. # Read images
  156. images = images2ims.readIms(os.path.join(tempDir, 'im*.jpg'), asNumpy)
  157. # Clean up
  158. _cleanDir(tempDir)
  159. # Done
  160. return images