Acabei escrevendo um script python para fazer isso
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
'''
converts sequence of images to video
uses glob pattern and allows skipping images
---
uses [sk-video](https://github.com/scikit-video/scikit-video)
'''> pip install sk-video'''
backend uses ffmpeg or libav
TODO:
- add more input & output dict opts
For full list of flags see
- https://ffmpeg.org/ffmpeg.html
- https://trac.ffmpeg.org/wiki/Encode/H.264
Note: Not using opencv2.VideoWriter because it fails silently on Anaconda
'''
from __future__ import print_function
from __future__ import division
import argparse
import glob
import os
from imageio import imread
from skvideo.io import FFmpegWriter
from tqdm import tqdm
from pprint import pprint
parser = argparse.ArgumentParser()
parser.add_argument('-i', '--input_folder', default='.', help='path to folder containing images')
parser.add_argument('-p', '--file_pattern', default='*.png', help='Unix filename pattern. For details see https://docs.python.org/2/library/fnmatch.html')
parser.add_argument('-o', '--output_path', default='output.mp4', help='path to output video file')
parser.add_argument('-n', '--every', default=10, type=int, help='include every nth image. i.e. every==1 uses every image. every==2 takes every other image ')
parser.add_argument('-m', '--max_count', default=None, type=int, help='if > 0, cap at this many frames')
parser.add_argument('-v', '--verbose', default=False, type=int, help='dump a lot of stuff to the console')
# ffmpeg args
parser.add_argument('-r', '--fps', default='30', type=str, help='frames per second')
parser.add_argument('-vcodec', default='libx264', type=str, help='output video codec')
parser.add_argument('-b', '--bitrate', default=None, type=str, help='bitrate')
parser.add_argument('-crf', default=None, type=str, help='Constant Rate Factor. The range of the CRF scale is 0–51, where 0 is lossless, 23 is the default, and 51 is worst quality possible. A lower value generally leads to higher quality, and a subjectively sane range is 17–28. Consider 17 or 18 to be visually lossless or nearly so; it should look the same or nearly the same as the input but it isnt technically lossless')
args = parser.parse_args()
#%% Get input file list
glob_path = os.path.join(os.path.expanduser(os.path.expandvars(args.input_folder)), args.file_pattern)
# read all files in folder matching pattern
print('Reading folder "{}"'.format(glob_path))
paths = sorted(glob.glob(glob_path))
args.stats = {'file_count_total' : len(paths) }
print(args.stats['file_count_total'], 'files found')
# skip
if args.every > 1:
paths = [x for i,x in enumerate(paths) if i % args.every == 0]
args.stats['file_count_with_skip'] = len(paths)
print(args.stats['file_count_with_skip'], 'files with skip')
# cap to max_count
if args.max_count:
paths = paths[:args.max_count]
args.stats['file_count_capped'] = len(paths)
print(args.stats['file_count_capped'], 'files after cap')
#%% init video writer with parameters
inputdict = {}
outputdict = {}
if args.fps:
inputdict['-r'] = args.fps
outputdict['-r'] = args.fps
if args.vcodec: outputdict['-vcodec'] = args.vcodec
if args.bitrate: outputdict['-b'] = args.bitrate
if args.crf: outputdict['-crf'] = args.crf
video_writer = FFmpegWriter(args.output_path, inputdict=inputdict, outputdict=outputdict)
#%%
print('Starting with args:')
pprint(vars(args))
for path in tqdm(paths):
img = imread(path)
if args.verbose: print('processing file', i, path)
video_writer.writeFrame(img)
print('\n-')
print('Finished. Video saved at', args.output_path)
video_writer.close()