Pythonで動画（MP4）の文字起こしをする

## はじめに
今回は、PythonでMP4ファイルの文字起こしについてのメモです。
Google Speech recognitionやFFmpegを使用しています。

## 実行環境
os：Windows10
Pythonバージョン：3.10
ffmpeg：4.2.2

## 処理の流れ
1．mp4ファイルをwavファイルに変換
2．wavファイルの内容をテキストファイルに記載
2-1．wavファイルを30秒単位に分割
2-2．wavファイルをテキストファイルに追記

## プログラムソース
```python:動画文字変換.py
<pre>
import wave
import struct
from scipy import fromstring,int16
import numpy as np
import os
import math
import speech_recognition as sr
import tkinter.filedialog
import ffmpeg 
import tkinter as tk
import tkinter.filedialog

#mp4からwavファイルへ変換する
def mp4towav(fileneme):
    mp4_file = fileneme
    wav_file = mp4_file.replace(".mp4",".wav")
    #wavファイルの存在チェック
    file = os.path.exists(wav_file)
    #wavファイルが存在する場合はスキップ
    if file == False:
        #保存先のディレクトリの作成
        os.mkdir(wav_dir)

        stream = ffmpeg.input(mp4_file) 
        # 出力 
        stream = ffmpeg.output(stream, wav_file) 
        # 実行
        ffmpeg.run(stream)
    
    else:
        print("処理をスキップしました")
    
    #変換後のファイル名を返す
    return wav_file

#filenameに読み込むファイル、timeにカットする間隔
def cut_wav(filename,time):  
    # timeの単位は[sec]

    # ファイルを読み出し
    wavf = filename
    wr = wave.open(wavf, 'r')

    # waveファイルが持つ性質を取得
    ch = wr.getnchannels()
    width = wr.getsampwidth()
    fr = wr.getframerate()
    fn = wr.getnframes()
    total_time = 1.0 * fn / fr 
    integer = math.floor(total_time*100) # 小数点以下切り捨て
    t = int(time*100)  # 秒数[sec]
    frames = int(ch * fr * t /100)
    num_cut = int(integer//t)
    # waveの実データを取得し、数値化
    data = wr.readframes(wr.getnframes())
    wr.close()
    X = np.frombuffer(data, dtype=int16)

    for i in range(num_cut + 1):
        print(i)
        # 出力データを生成
        outf = wav_dir + '/' + str(i) + '.wav' 
        #音声をカットした部分は少し巻き戻す
        if i > 0:
            start_cut = int(i*frames) - int(180000)
        else:
            start_cut = int(i*frames)

        end_cut = int(i*frames + frames)
        print(start_cut)
        print(end_cut)
        Y = X[start_cut:end_cut]
        outd = struct.pack("h" * len(Y), *Y)

        # 書き出し
        ww = wave.open(outf, 'w')
        ww.setnchannels(ch)
        ww.setsampwidth(width)
        ww.setframerate(fr)
        ww.writeframes(outd)
        ww.close()

        #音声ファイルをテキストファイルに変換
        wav_to_text(outf)

#音声ファイル（wav）から文字へ変換
def wav_to_text(wavfile):
    r = sr.Recognizer()

    #音声ファイルを指定 
    with sr.AudioFile(wavfile) as source:
        audio = r.record(source)
    
    try:
        #日本語変換
        text = r.recognize_google(audio, language='ja-JP')
        print(text)
                
        f = open(out_file, 'a')
        f.write(text)
        f.close
    # 以下は認識できなかったときに止まらないように。
    except sr.UnknownValueError:
        print("could not understand audio")
    except sr.RequestError as e:
        print("Could not request results from Google Speech Recognition service; {0}".format(e))

#文字起こしをするファイルを取得
#ファイルを選択するためにエクスプローラを開く(.mp4のもの)
fTyp = [("","*.mp4")]
#選択したファイル名を絶対パスで取得
iDir = os.path.abspath(os.path.dirname(__file__))
f_name = tk.filedialog.askopenfilename(filetypes=fTyp, initialdir=iDir)

#mp4からwavファイルに変換す
w_name = mp4towav(f_name)

# 一応既に同じ名前のディレクトリがないか確認
wav_dir = iDir + "/wav"
out_dir = iDir + "/output"
file = os.path.exists(wav_dir)
# print(file)
if file == False:
    #保存先のディレクトリの作成
    os.mkdir(wav_dir)

file = os.path.exists(out_dir)
if file == False:
    #保存先のディレクトリの作成
    os.mkdir(out_dir)

#wavファイルをcut_time単位に分割する
cut_time = 30
out_file = out_dir + '/out.txt'
cut_wav(w_name,float(cut_time))
```

# 参考記事
* [Pythonで音声ファイルを文字起こしする](https://qiita.com/daifuku10/items/62cc3d0075a6cb9389b4)
* [【簡単】Pythonで動画から音声（mp3・wav）を抽出する方法](https://self-development.info/%E3%80%90%E7%B0%A1%E5%8D%98%E3%80%91python%E3%81%A7%E5%8B%95%E7%94%BB%E3%81%8B%E3%82%89%E9%9F%B3%E5%A3%B0%EF%BC%88mp3%E3%83%BBwav%EF%BC%89%E3%82%92%E6%8A%BD%E5%87%BA%E3%81%99%E3%82%8B%E6%96%B9/)

# まとめ
動画のなかでボソボソ話している部分が認識できなかったり、
話し言葉や特殊な用語はうまく判断できない部分があります。
もともとは60秒単位で区切っていたのですが30秒単位に変更したところ
音声認識の制度が良くなったような気がします。（なぜかはわかりませんが・・）
今後も少しでも参考になるプログラムを投稿していけたらと思います。