power point によるプレゼンテーションから動画

(2024-10-08)

power point によるスライド作成から、それらの説明をテキストで書いて音声化して、スライドと音声をくっつけてさらにすべてを結合して動画にします。 そのプロセスを自動化して大量生産できるようにします。

power point によるスライド作成と書きましたが、実際には libre office impress で画像をエクスポートします。

構造

以下のような構造になっています。

「images」フォルダの中にスライドから作成した画像を入れます。

「texts」フォルダの中にそれぞれのスライドの説明文を入れます。

他のフォルダには音声ファイルと各動画ファイルが自動的に作成されます。また、結合された最終動画は「result」フォルダに作成されます。

スライド作成

最初は office 2000 professional の power point を使ってスライド作成しました。

しかし、全スライドを一括してエクスポートができないので、libre office で Export As Image というものをインストールして一括エクスポートします。

テキストファイルの作成

以下のようなテキストファイルを作成して、「texts」フォルダの中に「senario.txt」として保存します。
スライドの数に合わせて説明文を書きます。それぞれの説明文の間には空白行を2行以上作成します。

windows10に、office2000 professionalをインストールします。
windows10に古いoffice2000 professionalをインストールするのは大変です。
office2000のisoファイルとプロダクトキーがあってもインストールしようとすると、
エラーメッセージが出て、先に進めません。
そして、一度インストールを試みると、次からは見たこともないmicrosoftのアプリをインストールするよう促されます。
経験的に、これを実行するとハマります。
このエラーメッセージはレジストリに何かが記録されてしまったことが原因です。
そこで、レジストリを変更する必要があります。
windowsキーとRを押して、ファイル名を実行に「regedit」と入力してレジストリエディタを起動します。
そして、office関連のデータを削除します。
そうすると、インストール画面が再度表示されます。
エラーメッセージが表示されますが、再試行と無視をどんどん押せば最終的にインストールできます。
なおインストールするのは、wordとexcelとpower pointに限定すべきようです。

python プログラム

以下のような python プログラムを作成して実行します。

from gtts import gTTS
from datetime import datetime
from pydub import AudioSegment
from moviepy.editor import *
import shutil
import re
import os
import sys
import subprocess
import pathlib
class SoundSlide():
def __init__(self):
self.basicDir = '/home/mituo/デスクトップ/ドキュメント/vscode_python/Speaker/'
self.txtdir = self.basicDir + 'texts/'
self.sound_dir = self.basicDir + 'sounds/'
self.image_dir = self.basicDir + 'images/'
self.mp4_dir = self.basicDir + 'mp4/'
self.mergeResult = self.basicDir + 'result/'
self.txt = 'senario.txt'
self.tmpsound = 'tmp.mp3'
self.image_list = []
self.sound_list = []
self.textfile = self.txtdir + self.txt
self.totaltxt = ''
self.remove_file(self.sound_dir)
self.remove_file(self.mp4_dir)
def remove_file(self, path):
target_dir = pathlib.Path(path)
for file in target_dir.iterdir():
if file.is_file():
file.unlink()
def readtxt(self):
with open( self.textfile,'r',encoding="utf-8" ) as f:
s = f.read()
self.totaltxt = s
def check_number(self):
res = re.split('\n{2,}', self.totaltxt)
text_number = len([x for x in res if x])
image_number = sum(os.path.isfile(os.path.join(self.image_dir, name)) for name in os.listdir(self.image_dir))
if text_number != image_number:
print('イメージ数とテキスト数が異なっています。終了します。')
sys.exit()
def imagepath(self):
self.image_list = os.listdir(self.image_dir)
self.image_list.sort()
def create_sound(self):
res = re.split('\n{2,}', self.totaltxt)
for key, txt in enumerate(res):
if len(txt) > 0:
mp3name = format(key, '#02') + '.mp3'
tts = gTTS( txt, lang='ja', slow=False)
tts.save( self.sound_dir + self.tmpsound )
command = 'ffmpeg -y -i ' + self.sound_dir + self.tmpsound + ' -af atempo=1.3 ' + self.sound_dir + mp3name
subprocess.call(command, shell=True)
os.remove(self.sound_dir + self.tmpsound)
def soundpath(self):
self.sound_list = os.listdir(self.sound_dir)
self.sound_list.sort()
def ImgPlusSound(self):
self.soundpath()
for key, image_path in enumerate(self.image_list):
imagefile = self.image_dir + image_path
soundfile = self.sound_dir + self.sound_list[key]
mp4_path = self.mp4_dir + format(key, '#02') + '.mp4'
audio = AudioSegment.from_file(soundfile, 'mp3')
video = ImageClip(imagefile).set_duration(audio.duration_seconds)
video.write_videofile( mp4_path, fps=10, audio=soundfile)
def mergeMP4(self):
file_paths =[]
dt = datetime.now()
datetime_str = dt.strftime("%Y%m%d%H%M%S")
mergedMP4 = self.mergeResult + datetime_str + '.mp4'
mp4s = os.listdir(self.mp4_dir)
mp4s.sort()
for mp4 in mp4s:
if os.path.isfile(os.path.join( self.mp4_dir, mp4 )):
file_paths.append(os.path.join( self.mp4_dir, mp4 ))
clips = [VideoFileClip(file) for file in file_paths]
final_clip = concatenate_videoclips(clips)
final_clip.write_videofile( mergedMP4, codec="libx264")
if __name__ == '__main__':
ss = SoundSlide()
ss.readtxt()
ss.check_number()
ss.imagepath()
ss.create_sound()
ss.ImgPlusSound()
ss.mergeMP4()

結果

結果は以下のようになります。

エラーの可能性

画像と音声はそれぞれのフォルダを検索して対応させるのですが、10個以上になるときちんとしたソートをしないと対応が乱れる可能性があります。