上周一个学姐问我有没有兴趣毕业去她那做图像处理,抛给我个模式识别问题和一段4000+秒的mp4视频。大周末的我正犯五月病,就跟群里大佬问了下视频抓帧用什么合适,知道神奇的FFmpeg后顺手写了个python脚本做一下批量抓帧。至于为什么要用python不直接写shell,因为FFmpeg自带的批量抓帧命令是针对连续时间序列进行的,执行起来特别慢,用python是要做一下时间序列离散化,然后并行处理。
FFmpeg 先引用百度百科简单介绍下FFmpeg:
FFmpeg是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。采用LGPL或GPL许可证。它提供了录制、转换以及流化音视频的完整解决方案。它包含了非常先进的音频/视频编解码库libavcodec,为了保证高可移植性和编解码质量,libavcodec里很多codec都是从头开发的。
关于FFmpeg的业界地位,有很多视音频播放器是通过给FFmpeg加壳完成的。它是跨平台的,linux、win、mac os下都有发行版。想要安装,可以去官网 看看。关于文档,我找到的总结、教程、手册都比较零散,官方的英语文档又让新手无从看起,这次我只查到够用的资料就放着了,如果读者找到比较全面的实用手册,欢迎留言。
实现 我试着直接执行FFmpeg的批量抓帧命令时发现特别慢,几乎总要花费目标视频一半的播放时间。但是单张抓帧,不论时间点在哪里,其实都很快。于是我的思路是把视频的总时长拿出来,然后获得一个均匀分布的时间点集合,最后统统扔给gevent的pool并行抓帧。gevent是python一个著名的coroutine(协程)框架,初衷是处理高并发的网络IO,要安装pip一下就好。思路很简单,脚本也很短,life is short, I choose python!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 import osfrom commands import getoutputimport refrom gevent.pool import Pool as Gpoolfrom time import time file_path = '' ouput_path = '' interval = 1 while True : file_path = raw_input('Vedio path: ' ) if os.path.isfile(file_path): break else : print 'Not a file.' while True : ouput_path = raw_input('Output path( current directory for default ) : ' ) if ouput_path == '' : break if os.path.exists(ouput_path) and not os.path.isfile(ouput_path): ouput_path += '/' break else : print 'Not a directory.' while True : s = raw_input('Snap interval( 1 second for default ) : ' ) if s == '' : interval = 1 break if re.match (r'^[0-9]+(.[0-9]+){0,1}$' , s) is None : print 'Not a number.' else : interval = float (s) break info = getoutput('ffmpeg -i ' + file_path) dur = re.search(r'(?<=Duration: ).*?(?=,)' , info).group(0 ).split(':' ) dur = int (dur[0 ])*3600 + int (dur[1 ])*60 + float (dur[2 ])print 'Vedio duration: %.2f seconds.' % dur time_stamp_pool = [] time_stamp = 0 while time_stamp < dur: time_stamp_pool.append(time_stamp) time_stamp += interval gpool = Gpool() snap_cmd = 'ffmpeg -ss %f -i %s -nostats -loglevel 0 -q:v 2 -f image2 %s%d.jpg' n_snap = len (time_stamp_pool)print '%d frames to be snapped.' % n_snapprint 'Handling...' time0 = time()for i in xrange(n_snap): gpool.spawn(os.system, snap_cmd % (time_stamp_pool[i], file_path, ouput_path, i)) gpool.join() time_cost = time() - time0print 'Done in %.2f seconds.' % time_cost