# Copyright (C) 2012, Almar Klein # All rights reserved. # # This code is subject to the (new) BSD license: # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # * Neither the name of the nor the # names of its contributors may be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # # changes of this file GRASS (PNG instead of JPG) by Anna Petrasova 2013 """ Module images2avi Uses ffmpeg to read and write AVI files. Requires PIL I found these sites useful: http://www.catswhocode.com/blog/19-ffmpeg-commands-for-all-needs http://linux.die.net/man/1/ffmpeg """ import os import time import subprocess import shutil from grass.imaging import images2ims import grass.script as gscript def _cleanDir(tempDir): for i in range(3): try: shutil.rmtree(tempDir) except Exception: time.sleep(0.2) # Give OS time to free sources else: break else: print("Oops, could not fully clean up temporary files.") def writeAvi(filename, images, duration=0.1, encoding='mpeg4', inputOptions='', outputOptions='', bg_task=False): """Export movie to a AVI file, which is encoded with the given encoding. Hint for Windows users: the 'msmpeg4v2' codec is natively supported on Windows. Images should be a list consisting of PIL images or numpy arrays. The latter should be between 0 and 255 for integer types, and between 0 and 1 for float types. Requires the "ffmpeg" application: * Most linux users can install using their package manager * There is a windows installer on the visvis website :param str filename: output filename :param images: :param float duration: :param str encoding: the encoding type :param inputOptions: :param outputOptions: :param bool bg_task: if thread background task, not raise but return error message :return str: error message """ # Get fps try: fps = float(1.0 / duration) except Exception: raise ValueError(_('Invalid duration parameter for writeAvi.')) # Determine temp dir and create images tempDir = os.path.join(os.path.expanduser('~'), '.tempIms') images2ims.writeIms(os.path.join(tempDir, 'im*.png'), images) # Determine formatter N = len(images) formatter = '%04d' if N < 10: formatter = '%d' elif N < 100: formatter = '%02d' elif N < 1000: formatter = '%03d' # Compile command to create avi command = "ffmpeg -r %i %s " % (int(fps), inputOptions) command += "-i im%s.png " % (formatter,) command += "-g 1 -vcodec %s %s " % (encoding, outputOptions) command += "output.avi" # Run ffmpeg S = subprocess.Popen(command, shell=True, cwd=tempDir, stdout=subprocess.PIPE, stderr=subprocess.PIPE) # Show what ffmpeg has to say outPut = S.stdout.read() if S.wait(): # Clean up _cleanDir(tempDir) if bg_task: return gscript.decode(outPut) + '\n' + gscript.decode( S.stderr.read()) + '\n' + _('Could not write avi.') else: # An error occurred, show print(gscript.decode(outPut)) print(gscript.decode(S.stderr.read())) raise RuntimeError(_('Could not write avi.')) else: try: # Copy avi shutil.copy(os.path.join(tempDir, 'output.avi'), filename) except Exception as err: # Clean up _cleanDir(tempDir) if bg_task: return str(err) else: raise # Clean up _cleanDir(tempDir) def readAvi(filename, asNumpy=True): """Read images from an AVI (or MPG) movie. Requires the "ffmpeg" application: * Most linux users can install using their package manager * There is a windows installer on the visvis website :param str filename: name of input movie file :param bool asNumpy: """ # Check whether it exists if not os.path.isfile(filename): raise IOError('File not found: '+str(filename)) # Determine temp dir, make sure it exists tempDir = os.path.join(os.path.expanduser('~'), '.tempIms') if not os.path.isdir(tempDir): os.makedirs(tempDir) # Copy movie there shutil.copy(filename, os.path.join(tempDir, 'input.avi')) # Run ffmpeg command = "ffmpeg -i input.avi im%d.jpg" S = subprocess.Popen(command, shell=True, cwd=tempDir, stdout=subprocess.PIPE, stderr=subprocess.PIPE) # Show what mencodec has to say outPut = S.stdout.read() if S.wait(): # An error occurred, show print(outPut) print(S.stderr.read()) # Clean up _cleanDir(tempDir) raise RuntimeError("Could not read avi.") else: # Read images images = images2ims.readIms(os.path.join(tempDir, 'im*.jpg'), asNumpy) # Clean up _cleanDir(tempDir) # Done return images