ImageMagick
ffmpeg
jpeg

動画ファイルのスクリーンショットを横に並べた画像をいい感じに生成する

生きていると『動画ファイルの0.1秒ごとのスクリーンショットを10枚横に並べた画像を動画ファイルの秒数毎に作る』必要が出ることがごく稀にあります。しかもその画像ファイルはできる限りクオリティを下げずに容量を小さくしたいということがあります。

そんな時の方法を紹介します。

動画ファイルのスクリーンショットをいい感じに取る

ffmpegを使います。

TARGET=$1
TMPDIR=`mktemp -d`
# -ssが開始秒数 -tが取得秒数 -rが秒あたりのコマ数
ffmpeg -loglevel panic -ss 0 -i ${TARGET} -vsync 2 -r 10 -an -f image2 "${TMPDIR}/%03d.png"

これでffmpegを使って動画ファイルのスクリーンショットがいい感じに取得できます。ここでjpegではなくpngで出力しているのは画像の劣化を避けるためです。

画像をいい感じに結合していい感じに圧縮する

おなじみのImageMagickとmozilla/mozjpegを使います。動画ファイルの秒数は30秒だとします。

# ファイル名からディレクトリを作る
# cf: http://www.kishiro.com/FreeBSD/get_filename_in_shellscript.html
filename=${TARGET##*/}
TARGET_DIR=${filename%.*}
if [ -e ${TARGET_DIR} ]; then
  echo "${TARGET_DIR}ディレクトリが存在しています" 1>&2
  exit 1
fi
mkdir ${TARGET_DIR}

for i in {0..29}; do
  # ffmpegが01始まりでファイルを作るのでやむを得ずファイル名を全部書き出している
  # サンプリングやクオリティをいじって調整する
  convert -resize 6400x360 +append $(printf "${TMPDIR}/%02d1.png" ${i}) $(printf "${TMPDIR}/%02d2.png" ${i}) $(printf "${TMPDIR}/%02d3.png" ${i}) $(printf "${TMPDIR}/%02d4.png" ${i}) $(printf "${TMPDIR}/%02d5.png" ${i}) $(printf "${TMPDIR}/%02d6.png" ${i}) $(printf "${TMPDIR}/%02d7.png" ${i}) $(printf "${TMPDIR}/%02d8.png" ${i}) $(printf "${TMPDIR}/%02d9.png" ${i}) $(printf "${TMPDIR}/%02d0.png" $((i+1)))  pnm:- | cjpeg -sample 2x2,1x1,1x1 -progressive -optimize -quality 55 > $(printf "${TARGET_DIR}/%02d.jpg" ${i})
  # jpegtran
  jpegtran -copy none -optimize -outfile $(printf "${TARGET_DIR}/%02d.jpg" ${i}) $(printf "${TARGET_DIR}/%02d.jpg" ${i})
done

サンプリングについては以下のものが指定できます。詳しくは以下のURLを参考にしてください。

-sample 1x1,1x1,1x1
-sample 2x1,1x1,1x1
-sample 2x2,1x1,1x1
-sample 4x2,1x1,1x1
-sample 4x1,1x1,1x1

ImageMagick で JPEG の形式を変換 - awm-Tech

jpegtranを使うとjpegファイルのメタデータを削除してくれるので、不要なデータを削除することができます。画質は劣化しません。

皆様にそんな機会があるのかはよく分かりませんが、もしそういう機会があればぜひお使いください。