💍

MoviePyで結婚式のエンドロールムービーを作成する

2021/12/11に公開

私事ながら、先日結婚式を挙げさせて頂きました。
その中で、結婚式中に列席者の方々から送信して頂いた写真を使い、エンドロールを自動作成するという企画を行いました。

写真を送ってもらうためのLINE BOTや、列席者同士で共有できるためのサイトなど、いろいろ技術的なチャレンジをしたのですが、
今回はあらかじめ用意した写真をスライドショーとして加工し、動画に合成する方法をご紹介します。
また別の機会に全体の構成がわかる記事を書くつもりです。→書きました

MoviePy

MoviePyは動画編集のためのPythonライブラリです。動画のカット、連結、キャプション付与、エフェクトの追加などをはじめとしてさまざまな操作が可能です。
全てをPythonで記述できるため、スクリプトに組み込めば動画編集を自動化することができます。

なにを作ったのか

こんな感じの動画が出力されるスクリプトを作成しました。
冒頭で紹介した記事と同じく、MoviePyというPythonライブラリを使用しています。
こちらはサンプルですが、実際には48枚の写真が流れる4分ほどの動画に仕上がりました。

MoviePyでエンドロールを作成する

テンプレートとなる動画を用意する

まず写真を合成するためのテンプレートとなる動画を用意します。MoviePyで作らないのかよというツッコミが入りそうですが、自動化が必要な部分ではないので素直に動画編集ソフトで作ったほうが楽かと思います。
自分はAdobe Premiere Proの無料体験期間を利用して作成しました。
もちろんMoviePyでも作成可能です。

写真を用意する



続いて画像を用意します。今回はいらすとやの画像をローカルに保存して使います。
自動生成する場合には、スクリプトが実行される環境からアクセス可能な場所に画像がたまっていくシステムを別途構築する必要があります。

画像を動画に合成する

ここからMoviePyを使います。
まず、用意した画像と動画をMoviePyで扱えるようにするためにclipに変換します。
ImageClipを使って読み込んだあと、subclipによって5秒の動画clipに変換しています。
ここで指定した値が画像1枚あたりの表示時間になります。

from moviepy.editor import *
movie = VideoFileClip('template.mp4')
clip1 = ImageClip('image1.png').subclip(0, 5)
clip2 = ImageClip('image2.png').subclip(0, 5)
clip3 = ImageClip('image3.png').subclip(0, 5)

続いてclipに変換した画像のサイズ、位置、開始時間を調整します。
clip.wclip.hで縦横のサイズを参照できるので、動画に収まるように調節します。
また、set_positionを使ってちょうど良い位置に調節します。サイズは記事用の小さな動画に合わせたものになっているので、実際に使用する動画サイズに合わせて拡大してください。

set_startを使って動画中での開始時間を指定します。

from moviepy.video.fx.resize import resize
def create_effect(clip, start_time):
    center_w = 100
    center_h = 125

    # 画像の縦横の最大サイズ
    long_length = 180

    # 横長の画像の場合
    if clip.w >= clip.h:
        height = clip.h * long_length / clip.w
        width = long_length
    # 縦長の画像の場合
    else:
        width = clip.w * long_length / clip.h
        height = long_length

    margin_left = center_w - width / 2
    margin_top = center_h - height / 2
    clip = resize(clip, (width, height))
    clip = clip.set_position((margin_left, margin_top))
    clip = clip.set_start(start_time)
    return clip

movie_parts = [create_effect(clip, i * 5) for i, clip in enumerate([clip1, clip2, clip3])]

あとは動画に合成すれば完成です。

video = CompositeVideoClip([movie] + movie_parts)
video.write_videofile('movie.mp4', fps=30)

画像にエフェクトをつける

さて、このままだと画像が単調に切り替わっていくだけであまりにも面白みにかけます。
もう少しスライドショーっぽくするために画像にフェードイン/フェードアウトのエフェクトを付けてみましょう。

やり方は簡単で、fadein fadeoutにclipを渡してあげるだけです。
引数にフェードイン/フェードアウトの持続時間を指定します。

from moviepy.video.fx.all import fadein
from moviepy.video.fx.all import fadeout
clip = fadein(clip, 1)
clip = fadeout(clip, 1)

先程のコードは以下のように書き換えられます。

from moviepy.video.fx.resize import resize
from moviepy.video.fx.all import fadein
from moviepy.video.fx.all import fadeout

def create_effect(clip, start_time):
    center_w = 100
    center_h = 125

    # 画像の縦横の最大サイズ
    long_length = 180

    # 横長の画像の場合
    if clip.w >= clip.h:
        height = clip.h * long_length / clip.w
        width = long_length
    # 縦長の画像の場合
    else:
        width = clip.w * long_length / clip.h
        height = long_length

    margin_left = center_w - width / 2
    margin_top = center_h - height / 2
    clip = resize(clip, (width, height))
    
    # フェードイン/フェードアウトをかける
    clip = fadein(clip, 1)
    clip = fadeout(clip, 1)
    clip = clip.set_position((margin_left, margin_top))
    clip = clip.set_start(start_time)
    return clip

movie_parts = [create_effect(clip, i * 5) for i, clip in enumerate([clip1, clip2, clip3])]

そこそこエンドロールっぽい出来上がりになりましたね。
あとは仲間同士の楽しそうな写真やBGMを使えば、なかなかエモい仕上がりになると思います!

MoviePyでのエンドロール作成を検討している方の参考になれば幸いです!

明日は@kinari321さんのpaizaのスキルチェックを200問以上解いて分かったことです。お楽しみに!

Discussion