Last active
August 19, 2025 17:13
-
-
Save alexflint/9609202 to your computer and use it in GitHub Desktop.
Create a video from a sequence of images (or PDFs) in python (using ffmpeg internally)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import os | |
| import sys | |
| import tempfile | |
| import argparse | |
| import subprocess | |
| NATURAL_FPS = 30. | |
| # Formats convertible by imagemagick: | |
| CONVERTIBLE_EXTENSIONS = ['png', 'jpeg', 'jpg', 'pdf', 'pgm', 'bmp'] | |
| # Formats readable by ffmpeg: | |
| FFMPEG_EXTENSIONS = ['png', 'jpg', 'jpeg'] | |
| def extension(path): | |
| return os.path.splitext(path)[1][1:].lower() | |
| def main(): | |
| parser = argparse.ArgumentParser(description='Make a video from a sequence of video frames') | |
| parser.add_argument('--limit', type=int, required=False) | |
| parser.add_argument('--fps', type=float, default=NATURAL_FPS) | |
| parser.add_argument('input', nargs='+', | |
| help='Images or PDFs from which to create the video, or a directory '+\ | |
| 'containing images or pdfs') | |
| parser.add_argument('output', type=str) | |
| args = parser.parse_args() | |
| if os.path.exists(args.output): | |
| ext = extension(args.output) | |
| if ext in CONVERTIBLE_EXTENSIONS: | |
| print 'Error: Cannot overwrite with output: %s' % args.output | |
| return -1 | |
| # Process input paths | |
| input_paths = [] | |
| for path in args.input: | |
| if os.path.isdir(path): | |
| for entry in sorted(os.listdir(path)): | |
| if extension(entry) in CONVERTIBLE_EXTENSIONS: | |
| input_paths.append(os.path.join(path, entry)) | |
| else: | |
| input_paths.append(path) | |
| if args.limit: | |
| input_paths = input_paths[:args.limit] | |
| # Pick an intermediate format to convert the frames to | |
| temp_ext = extension(input_paths[0]) | |
| if temp_ext not in FFMPEG_EXTENSIONS: | |
| print '%s files are not readable by ffmpeg: will convert to png.' % temp_ext | |
| temp_ext = 'png' | |
| # Create symlinks | |
| tempdir = tempfile.mkdtemp() | |
| index = 0 | |
| for index, input_path in enumerate(input_paths): | |
| ext = extension(input_path) | |
| temp_path = os.path.join(tempdir, '%08d.%s' % (index, temp_ext)) | |
| if ext == temp_ext: | |
| os.symlink(input_path, temp_path) | |
| else: | |
| print 'Converting %s to %s' % (input_path, temp_ext) | |
| retval = subprocess.call(['convert', input_path, temp_path]) | |
| if retval: | |
| print 'Unable to convert %s to %s' % (input_path, temp_ext) | |
| return -1 | |
| # Create video | |
| print '\nRunning ffmpeg:' | |
| ffmpeg_options = { | |
| '-filter:v' : 'setpts=%f*PTS' % (NATURAL_FPS / args.fps), | |
| '-b:v' : '1000k', | |
| '-f': 'image2', | |
| '-i': os.path.join(tempdir, '%%08d.%s' % temp_ext) | |
| } | |
| command = ['ffmpeg'] | |
| for key, value in ffmpeg_options.iteritems(): | |
| command.extend((key, str(value))) | |
| command.append('-y') # overwrite outputs | |
| command.append(args.output) | |
| print ' '.join(command) | |
| subprocess.call(command) | |
| print '\nWrote output to %s' % args.output | |
| return 0 | |
| if __name__ == '__main__': | |
| sys.exit(main()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
ভিডিও দেও