Elsaの技術日記(徒然なるままに)

主に自分で作ったアプリとかの報告・日記を記載

MENU

pythonで複数枚の写真からスライド動画を作成

今回は複数の写真をつなぎ合わせ、スライドショーのような動画を作成していきます。
写真だけ眺めていてもちょっと味気なくなってきたのでスライドショーを作成することにしました!!
さらに、ただ写真がスライドショー形式で表示されても、、、なので、
作成したスライドショーに音楽も追加してみました!!

今後も何かで使えそうな技術なので備忘録として残しておこうと思います。



■環境準備

まずは今回使用する環境です。
言語はタイトルの通りpythonを持ちますが、バージョンは
Python 3.8.2
を用いました。
Python 3.7.3でも試しに動かしたのですが問題なく動きました。

今回動画作成にあたり用いたライブラリは、
"MoviePy"
です。

【MoviePyとは?】
カット、連結、タイトル挿入、ビデオ合成、ビデオ処理、エフェクト追加・作成などのビデオ編集をPythonで行うためのライブラリになります。
MoviePyはFFmpegを利用して画像・動画の編集を行っています。
FFmpegですが、MoviePyをインストールした際に自動でインストールされるようです。
WindowsmacOSLinuxでサポートされているようです。
さらに、サポート対象のPythonバージョンは3.6以降とのこと。
先ほど記載しました通り、私の環境は3.6以上になりますので問題なく動作することになります。


MoviePyのインストールですが、下記コマンドを実行すればOKです。

pip install moviepy
or 
pip3 install moviepy

インストール完了するまでに少し待ちますので、コーヒーでも飲みながら気長に待ってみてください。

これで環境のセットアップは完了です。

■複数枚の写真から動画を作成

では本題の複数枚の写真から動画を作成してみます。
早速ですが、コードはこちら。

import glob
from moviepy.editor import *
import os

def MakeImgToMovie():
    file_list = glob.glob(r'/写真までのパス/*.jpg')
    file_list.sort()
    clips = []
    for m in file_list:
        clip = ImageClip(m).set_duration('00:00:04')  #画像が表示され続ける時間(今回は4秒)
        clip = clip.crossfadein(0.5)                              #画像表示時のアニメーションとして0.5秒のフェードインを追加
        clip = clip.crossfadeout(0.5)                           #画像が消える時のアニメーションとして0.5秒のフェードアウトを追加
        clip = clip.resize(newsize=(Width,Height))    #動画化する際の画像サイズをリサイズ
        clips.append(clip)                                           
    
    concat_clip = concatenate_videoclips(clips, method="compose")
    concat_clip.write_videofile(r"作成先動画パス",fps=24)

まず、globライブラリを用いて複数写真(画像)をファイルリストとして読み出します。
合わせて、読み出し順が崩れないようにソートも実行。

file_list = glob.glob(r'/写真までのパス/*.jpg')
file_list.sort()

次にスライドショーにするために各画像のアニメーションや表示時間を設定していきます。

    for m in file_list:
        clip = ImageClip(m).set_duration('00:00:04')  #画像が表示され続ける時間(今回は4秒)
        clip = clip.crossfadein(0.5)                              #画像表示時のアニメーションとして0.5秒のフェードインを追加
        clip = clip.crossfadeout(0.5)                           #画像が消える時のアニメーションとして0.5秒のフェードアウトを追加
        clip = clip.resize(newsize=(Width,Height))    #動画化する際の画像サイズをリサイズ
        clips.append(clip)    

set_durationを用いて画像が表示され続ける時間の長さを調整。
crossfadein、crossfadeoutにより画像切り替え時に0.5秒のフェードイン・フェードアウトを追加。
そして、resize(newsize=(Width,Height))で動画化した時の画像サイズを指定画像サイズにリサイズ。

ここで、00:00:04や0.5、Width,Heightには好きな数値を入れてください。
ただし、これらの数値が大きいほど動画作成にかなりの時間を要することになるため注意が必要です。

最後に、

concat_clip = concatenate_videoclips(clips, method="compose")
concat_clip.write_videofile(r"作成先動画パス",fps=24)

にて、編集した画像群を動画として結合し動画化します。
write_videofileにて動画のfpsもセットできます(今回は24fpsに設定してみています)。

■MoviePyは動画生成が遅い?

ではコードもでき、画像も準備出来たので早速実行!!
してみたのですが、、どうにも処理速度が遅い。。
FFmpegを用いていることので、
write_videofile関数の引数にcodecを指定してハードウェアエンコードも試したのですが結果は変わらず。。

調べてみたところ、どうやらMoviePyライブラリ内のwrite_videofile関数に問題があるようで、
実行後、リソース開放が完了するまでの間待ち続けてしまうとのこと。

少しでも早くするためには画像サイズや画像表示時間を短くすることで、
実際の画像処理にかかる時間を縮めるないしは使用するフレーム数を減らす必要があるようです。
まぁ、ハイスペックPCであれば短くなるのでしょうが。。

■動画に音楽を付与する

では先ほど作成した動画に音楽を付与してみます。
コードはこちら。

def AddAudioToMovie():
    clip = VideoFileClip(r"動画パス")
    audioclip = AudioFileClip(r"音楽パス")
    new_videoclip = clip.set_audio(audioclip)
    new_videoclip.write_videofile(r"合成した動画保存先")

解説することがないくらいですが、、
まず、

    clip = VideoFileClip(r"動画パス")
    audioclip = AudioFileClip(r"音楽パス")

で合成させたい動画と音楽をVideoFileClip、AudioFileClipで読み出します。

そして、

new_videoclip = clip.set_audio(audioclip)

にて動画と音声を結合。

最後に、

new_videoclip.write_videofile(r"合成した動画保存先")

で保存先に動画を保存します。

■作成してみた

では、先ほどのコードで作成した動画の一部を下記に載せます。
f:id:Elsammit:20210829152937g:plain
確かにスライドショーのように動画が切り替わっていることが分かるかと思います。

■最後に

ライブラリを使用すること簡単に複数写真からスライドショーのような動画を作成してみました。
作ってみた感想としては、簡単に作れるけど凝ったことを使用とすると難しかったり手間がかかるので商用のビデオツールはやはり優秀だな、と感じたことです。
う~~ん。
やはり編集ソフト持っていたほうが良いかな。。
まぁ、作るの好きだし苦ではないんですがw