作成日: 2024-08-11
更新日: 2024-08-02

pythonでaudioの波形を取得して、簡単なlip syncを実現する

pythonでaudioの波形を取得して、簡単なlip syncを実現する

謝辞

最初に謝辞

東北ずんこ:ずんだもんプロジェクト

https://zunko.jp/

ずんだもんの声はVOICEVOXを利用させて頂いています。

https://voicevox.hiroshiba.jp/

ずんだもんの立ち絵は坂本アヒル様の立ち絵を利用させて頂いております。

https://www.pixiv.net/artworks/92641351

今回のアウトプットで使用させて頂きました。 ありがとうございました。

それから、以下の記事を参考にさせて頂きました。

https://c-a-p-engineer.github.io/tech/2024/04/28/python-lip-sync/

どうもありがとうございました。

パッケージのインストール

まず、必要なパッケージをpipでインストールします。

pip install librosa moviepy opencv-python Pillow

実装

次にコード。

import cv2
import librosa
import numpy as np
import os
import sys
from moviepy.editor import *
from PIL import Image, ImageDraw, ImageFont, ImageOps

# リップシンク動画の作成
def wav_to_mp4(input_wav_path, output_mp4_path):
    directory = os.path.dirname(input_background_path)
    temp_dir = os.path.join(directory, "temp_dir")
    lip_sync_output_path = os.path.join(directory, "_lip_sync.mp4")

    # 画像ファイルを読み込み
    mouth_close_input_path = "images/default.png"
    mouth_close_image = cv2.imread(mouth_close_input_path, cv2.IMREAD_UNCHANGED)  # アルファチャンネルを含む
    mouth_close_width = mouth_close_image.shape[1]
    mouth_close_height = mouth_close_image.shape[0]

    mouth_open_input_path = "images/mouth_opened.png"
    mouth_open_image = cv2.imread(mouth_open_input_path, cv2.IMREAD_UNCHANGED)  # アルファチャンネルを含む
    mouth_open_width = mouth_open_image.shape[1]
    mouth_open_height = mouth_open_image.shape[0]

    fourcc = cv2.VideoWriter_fourcc(*'mp4v')
    video = cv2.VideoWriter(output_mp4_path, fourcc, 30.0, (mouth_close_image.shape[1], mouth_close_image.shape[0]))

    # 画像ファイルの存在を確認
    if not os.path.exists(mouth_close_input_path) or not os.path.exists(mouth_open_input_path):
        logging.error("Mouth image files not found. Check the paths.")
        sys.exit(1) # エラーが発生した場合は終了せずに戻る

    # 音声ファイルを読み込み、サンプリングレートをそのままにして音質を保持
    y, sampling_rate = librosa.load(input_wav_path, sr=None)

    # 無音区間を検出
    # 非無音セグメントを取得
    # top_dbを15に設定。音源によって調整が必要。
    segments = librosa.effects.split(y, top_db=15, ref=np.max)

    # 各フレームに対して画像を選択し、ビデオフレームに追加
    t = 0
    frame_rate = 30
    frame_duration = 1 / frame_rate
    total_duration = len(y) / sampling_rate
    while t < total_duration:
        segment_status = any(start / sampling_rate <= t < end / sampling_rate for start, end in segments)
        frame = mouth_open_image if segment_status else mouth_close_image # 音を検出していたらmouth_open_imageを、検出していなければmouth_close_imageをframeに設定
        video.write(frame)
        t += frame_duration
    video.release()

    # moviepyを使ってビデオファイルに音声ファイルを結合
    video_clip = VideoFileClip(output_mp4_path)
    audio_clip = AudioFileClip(input_wav_path)
    final_clip = video_clip.set_audio(audio_clip)
    final_clip.write_videofile(output_mp4_path, codec='libx264')

このコードを実行すると、以下の動画を作成することができます。

おしまい。