FFMPEG:从可变长度的视频中提取20张图像



我已经在网上浏览了这个非常密集,但我没有找到我需要的,只有它的变化,这不是我想要使用的东西。

我有几个不同长度的视频,我想从每个视频从头到尾提取20个图像,以显示视频的最广泛印象。

所以一个视频是16米47秒长=>共1007s =>我必须每隔50秒给视频拍一张快照。

所以我想使用ffmpeg的-r开关,值为0.019860973 (eq 20/1007),但ffmpeg告诉我帧率太小了…

我想要做的唯一方法是写一个脚本,调用ffmpeg与操纵-ss开关和使用-vframes 1,但这是相当慢的,有点离我,因为ffmpeg计算图像本身…

我也在努力寻找这个问题的答案。我用了radri的答案,但发现它有一个错误。

ffmpeg -i video.avi -r 0.5 -f image2 output_%05d.jpg

每2秒生成一帧,因为-r表示帧速率。在这种情况下,每秒0.5帧,或者每2秒1帧。

用同样的逻辑,如果你的视频是1007秒长,你只需要20帧,你需要每50.53秒一帧。换算成帧速率,应该是每秒0.01979帧。

所以你的代码应该是
ffmpeg -i video.avi -r 0.01979 -f image2 output_%05d.jpg

我希望这能帮助到别人,就像它帮助了我一样。

我知道我来晚了一点,但我想这可能会有帮助:

对于每个人都有ffmpeg为每一帧生成图像的问题,以下是我如何解决它(使用blahdiblah的答案):

首先,我抓取了视频中的总帧数:

ffprobe -show_streams <input_file> | grep "^nb_frames" | cut -d '=' -f 2

然后我尝试使用select来抓取帧:

ffmpeg -i <input_file> -vf "select='not(mod(n,100))'" <output_file>

但是,无论mod(n,100)设置为什么,ffmpeg都会输出太多帧。我必须添加-vsync 0来纠正它,所以我的最终命令看起来像这样:

ffmpeg -i <input_file> -vsync 0 -vf "select='not(mod(n,100))'" <output_file>

(其中100是你想要使用的帧频率,例如,每100帧)

希望这能给某人省去一点麻烦!

您可以尝试将视频转换为N张图像吗?

ffmpeg -i video.avi image%d.jpg
更新:

或者每2秒提取一帧:

ffmepg -i video.avi -r 0.5 -f image2 output_%05d.jpg
更新:

获取视频时长:

ffmpeg -i video.avi 2>&1 | grep 'Duration' | cut -d ' ' -f 4 | sed s/,//

然后,根据你的编程语言,你把它转换成秒。例如,在PHP中,您可以这样做:

$duration = explode(":",$time); 
$duration_in_seconds = $duration[0]*3600 + $duration[1]*60+ round($duration[2]);

,其中$time为视频时长。你可以执行$duration_in_seconds/20

ffmepg -i video.avi -r $duration_in_seconds/20 -f image2 output_%05d.jpg

一般来说,ffmpeg在帧到来时处理帧,因此基于总持续时间/帧总数的任何内容都需要一些预处理。

我建议编写一个简短的shell脚本,使用类似 的东西来获取帧总数。
ffprobe -show_streams <input_file> | grep "^nb_frames" | cut -d '=' -f 2

,然后使用select视频滤波器挑选出您需要的帧。所以ffmpeg命令看起来像

ffmpeg -i <input_file> -vf "select='not(mod(n,100))'" <output_file>

只是100将被shell脚本中计算的数字替换为20帧,而不是每100帧。

我有同样的问题,并提出了这个脚本,似乎做的伎俩:

#/bin/sh
total_frames=`ffmpeg -i ./resources/iceageH264.avi -vcodec copy -acodec copy -f null dev/null 2>&1 | grep 'frame=' | cut -f 3 -d ' '`
numframes=$3 
rate=`echo "scale=0; $total_frames/$numframes" | bc`
ffmpeg -i $1 -f image2 -vf "select='not(mod(n,$rate))'" -vframes $numframes -vsync vfr $2/%05d.png

要使用它,请将其保存为.sh文件,并使用以下参数运行它以导出20帧:

./getFrames.sh ~/video.avi /outputpath 20

这将为您提供在整个视频中均匀分布的指定帧数。

我有一个类似的问题,即如何在长度未知的电影中提取一帧。感谢这里的答案,我想出了这个解决方案,它确实工作得很好,我认为张贴php代码将是有用的:

<?php 
header( 'Access-Control-Allow-Origin: *' );
header( 'Content-type: image/png' );
// this script returns a png image (with dimensions given by the 'size' parameter as wxh)
// extracted from the movie file specified by the 'source' parameter
// at the time defined by the 'time' parameter which is normalized against the 
// duration of the movie i.e. 'time=0.5' gives the image halfway the movie.
// note that this can also be done using php-ffmpeg..
$time = floatval( $_GET[ 'time' ] );
$srcFile = $_GET[ 'source' ];
$size = $_GET[ 'size' ];
$tmpFile = tempnam( '/tmp', 'WTS.txt' );
$destFile = tempnam( '/tmp', 'WTS.png' );
$timecode = '00:00:00.000';
// we have to calculate the new timecode only if the user 
// requests a timepoint after the first frame
if( $time > 0.0 ){
    // extract the duration via a system call to ffmpeg 
    $command = "/usr/bin/ffmpeg -i ".$srcFile." 2>&1 | grep 'Duration' | cut -d ' ' -f 4 | sed s/,// >> ".$tmpFile;
    exec( $command );
    // read it back in from tmpfile (8 chars needed for 00:00:00), skip framecount
    $fh = fopen( $tmpFile, 'r' );
    $timecode = fread( $fh, 8 );
    fclose( $fh );
    // retieve the duration in seconds
    $duration = explode( ":", $timecode ); 
    $seconds = $duration[ 0 ] * 3600 + $duration[ 1 ] * 60 + round( $duration[ 2 ] );
    $timepoint = floor( $seconds * $time );
    $seconds = $timepoint % 60;
    $minutes = floor( $timepoint / 60 ) % 60;
    $hours = floor( $timepoint / 3600 ) % 60;
    if( $seconds < 10 ){ $seconds = '0'.$seconds; };
    if( $minutes < 10 ){ $minutes = '0'.$minutes; };
    if( $hours < 10 ){ $hours = '0'.$hours; };
    $timecode = $hours.':'.$minutes.':'.$seconds.'.000';
}
// extract an image from the movie..
exec( '/usr/bin/ffmpeg -i '.$srcFile.' -s '.$size.' -ss '.$timecode.' -f image2 -vframes 1 '.$destFile );
// finally return the content of the file containing the extracted frame
readfile( $destFile );
?> 

我也有同样的问题,使用选择过滤器对我不起作用,对于大型视频的使用率非常慢,所以我用python编写了这个脚本

它所做的是以秒为单位的持续时间,根据屏幕截图的数量计算间隔,最后在输入文件

之前使用seek在每个间隔内截取屏幕截图
#!/usr/bin/python3
import re
from os import makedirs
from os.path import basename, join, dirname, isdir
from sys import argv, exit
from subprocess import call, run, Popen, PIPE, STDOUT, CalledProcessError
from ffmpy3 import FFmpeg, FFRuntimeError, FFExecutableNotFoundError
argv.pop(0)
for ifile in argv:
    #Get video info
    duration = run(['ffprobe', '-hide_banner', '-i', ifile], stderr=PIPE, universal_newlines=True)
    #Extract duration in format hh:mm:ss
    duration = re.search(r'Duration: (d{2}:d{2}:d{2}.d{2})', duration.stderr)
    #Convert duration to seconds
    duration = sum([a*b for a,b in zip([3600, 60, 1],
                    [float(i) for i in duration.group(1).split(':')])])
    #Get time interval to take screenshots
    interval = int (duration / 20)
    fname = basename(ifile)[:-4]
    odir = join(dirname(ifile),fname)
    if not isdir(odir):
        makedirs(odir, mode=0o750)
    ofile = join(odir,fname)
    #Take screenshots at every interval
    for i in range (20):
        run([])
        ff = FFmpeg(
            global_options='-hide_banner -y -v error',
            inputs={ifile: f'-ss {i * interval}'},
            outputs={f'{ofile}_{i:05}.jpg': f'-frames:v 1 -f image2'}
            )
        ff.run()

我就这样做了,而且百分之百成功。

$time = exec("ffmpeg -i input.mp4 2>&1 | grep 'Duration' | cut -d ' ' -f 4 | sed s/,//");
$duration = explode(":",$time);
$duration_in_seconds = $duration[0]*3600 + $duration[1]*60+ round($duration[2]);
$duration_in_seconds = $duration_in_seconds/20;
exec("ffmpeg -i input.mp4 -r 1/$duration_in_seconds thumb_%d.jpg");

最新更新