web-dev-qa-db-ja.com

FFMPEG:可変長のビデオから20枚の画像を抽出する

私はこれを非常に集中的にインターネットで閲覧しましたが、必要なものが見つかりませんでした。私が使いたいものではないバリエーションだけが見つかりました。

長さの異なる動画がいくつかありますが、最初から最後まですべての動画から20枚の画像を抽出して、動画の最も広い印象を示したいと思います。

したがって、1つのビデオの長さは16分47秒=>合計1007秒=> 50秒ごとにビデオのスナップショットを1つ作成する必要があります。

だから私は0.019860973(eq 20/1007)の値でffmpegの-rスイッチを使用して考えましたが、ffmpegはフレームレートが小さすぎることを教えてくれます...

私がそれを行うために考え出した唯一の方法は、操作された-ssスイッチでffmpegを呼び出し、-vframes 1を使用するスクリプトを書くことですが、ffmpegsは画像自体に数値を付けるため、これは非常に遅く、少しずれています...

何か提案や指示はありますか?

ありがとう、Vapire

15
Vapire

私もこの質問に対する答えを見つけようとしていました。ラドリの答えを利用しましたが、間違いがあることがわかりました。

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

-rはフレームレートを意味するため、2秒ごとにフレームを生成します。この場合、1秒あたり0.5フレーム、または2秒ごとに1フレームです。

同じロジックで、ビデオの長さが1007秒で、必要なフレームが20フレームしかない場合は、50.53秒ごとにフレームが必要です。フレームレートに変換すると、1秒あたり0.01979フレームになります。

だからあなたのコードは

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

それが私を助けたように、それが誰かを助けることを願っています。

15
Terrence Tan

私はパーティーに少し遅れていることを知っていますが、これが役立つかもしれないと思いました:

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はあまりにも多くのフレームを吐き出していました。これを修正するには-vsync0を追加する必要があったため、最終的なコマンドは次のようになりました。

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

(100は、たとえば100フレームごとに使用するフレーム周波数です)

それが誰かに少しのトラブルを救うことを願っています!

16
GhostCode

ビデオを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
6
radri

一般に、ffmpegはフレームが来ると処理するため、合計期間/合計フレーム数に基づくものはすべて前処理が必要です。

次のようなものを使用してフレームの総数を取得するには、短いシェルスクリプトを作成することをお勧めします。

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

次に、 select video filter を使用して、必要なフレームを選択します。したがって、ffmpegコマンドは次のようになります

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

ただし、100フレームごとではなく、100は、シェルスクリプトで計算された数に置き換えられ、合計20フレームになります。

5
blahdiblah

私は同じ問題を抱えていて、トリックを行うように見えるこのスクリプトを思いついた:

#/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

これにより、指定された数のフレームがビデオ全体に均等に分散されます。

3
user1063552

同様の質問がありました。つまり、長さが不明な映画の途中で1つのフレームを抽出する方法です。ここでの回答のおかげで、私は実際に非常にうまく機能するこのソリューションを思いつきました。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 );
?> 
1