【Pythonで音声ファイルの文字起こし】Jupyterlabで、自動翻訳まで実行させる方法

はじめに

会議などで録音した音声ファイルをPythonで自動的に文字起こしできないものか、と誰かが耳元でつぶやいたので調べてみたところ、既に先達がいて見事なコードを公開されていたのでそれを有り難く利用させてもらいつつ、ついでに英語翻訳も自動でさせてしまおう、ということでそのコードを公開してみる。

DockerにてPythonを動かす環境を構築するので、そのDocker ImageとContainerを作成するDockerfileとDocker Composeも公開する。

またPythonの記述はJupyterlabで行うが、その際参照元のPythonコードの1行だけ変更する必要があるので、それについても言及する。



ディレクトリ

desktop
  └python_transcriptionフォルダ
       ├Docker Composeファイル
       ├Dockerfile
       └workフォルダ
              └ipynbファイル(jupyterlabで作成・編集)
              └waveファイル(文字起こしする音声ファイル)
             (└outputフォルダ(コードを走らせると自動生成される))


(開始前のディレクトリ画像)


(作成後のディレクトリ画像)


拝借したコードの公開元

公開元は、以下のサイト。

Pythonで音声ファイルを文字起こしする - Qiita

会議議事録作成など、音声ファイルをPythonとGoogle Speech recognitionを使って、文字起こしにチャレンジします。 Google Speech recognitionは無料で使えるのは1~3分程...

このサイトの「プログラムソース」内「音声文字変換3.py」を元に、Jupyterlab用に一部コードを修正し、かつ英語翻訳用のライブラリとコードなどを追加していった。



各コードの完成版

Dockerfile完成版

FROM ubuntu:latest

ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update && apt-get install -y \
		python3 \
		python3-pip \
		libx11-dev \
		python3-tk

RUN pip3 install jupyterlab \
		scipy \
		numpy \
		SpeechRecognition \
		googletrans==4.0.0-rc1 \
		deepl \
		openpyxl \
		pandas


Docker Compose完成版

version: "3.8"
services:
  web:
    build:
      context: .
      dockerfile: Dockerfile
    environment:
      - DISPLAY=host.docker.internal:0.0
      - DEEPL_AUTH_KEY=◆◆◆◆◆◆◆◆◆◆◆ # deepL APIに登録後取得した認証キーをここに追加
    volumes:
      - ./work:/work

    ports:
      - 8000:8888
    image: python_ts:latest
    container_name: python_transcription
    command:
      jupyter lab
        --ip=0.0.0.0
        --allow-root
        --notebook-dir='/work'
        --LabApp.token=''


Pythonコード完成版

以下の各コードで追加修正した行には「# ●●●●●:今回追加分」または「# ●●●●●:今回修正分」とコメントしている

import wave
import struct
import os
import math
import tkinter.filedialog
from pathlib import Path # 音声ファイル読込時のファイルパス選定用:今回追加分

# 以下は外部ライブラリ(要インストール:Dockerfileにてインストールするよう記述)
from scipy import fromstring,int16
import numpy as np
import speech_recognition as sr
from googletrans import Translator # 翻訳用(google):今回追加分
import deepl # 翻訳用(deepL):今回追加分
import openpyxl # Excelファイルを生成するときに必要:今回追加分
import pandas as pd

# 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):
        # 出力データを生成
        outf = out_dir + '/' + str(i) + '.wav' 
        # 音声をカットした部分は少し巻き戻す;今回は巻き戻さないので以下3行コメントアウトする:今回修正分
        #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()

    str_out = ""
    str_tr_out = ""# 翻訳用:今回追加分;翻訳不要の場合はコメントアウトする
    #list1 = [wavf,"",""] # 翻訳不要の場合はこちらを使用し、下記をコメントアウトする
    list1 = [wavf,"","","",""] # 翻訳用;上記に「,"",""」を追加:今回追加分
    df_x = pd.DataFrame([list1])
    #df_x.columns = ['No', '音声ファイル', '変換結果'] # 翻訳不要の場合はこちらを使用し、下記をコメントアウトする
    df_x.columns = ['No','音声ファイル','変換結果','ggl翻訳','dpl翻訳'] # 翻訳用;上記に「,'ggl翻訳','dpl翻訳'」を追加:今回追加分

    for ii in range(num_cut + 1):
        outf = out_dir + '/' + str(ii) + '.wav' 
        str_out = wav_to_text(outf)
        str_ggl_out = Translate_ggl(str_out) # str_outをggl翻訳:今回追加分;翻訳不要の場合はコメントアウトする
        str_dpl_out = Translate_dpl(str_out) # str_outをdpl翻訳:今回追加分;翻訳不要の場合はコメントアウトする
        #df_x.loc[ii] = [ii,str(ii) + '.wav',str_out] # 翻訳不要の場合はこちらを使用し、下記をコメントアウトする
        df_x.loc[ii] = [ii,str(ii) + '.wav',str_out,str_ggl_out,str_dpl_out] # 上記に「,str_ggl_out,str_dpl_out」を追加:今回追加分

    # excelへ書き出し
    with pd.ExcelWriter(out_file) as writer:
        df_x.to_excel(writer, sheet_name='結果', index=False)

def wav_to_text(wavfile):
    r = sr.Recognizer()

    with sr.AudioFile(wavfile) as source:
        audio = r.record(source)

    wav_to_text = r.recognize_google(audio, language='ja-JP')

    print(wav_to_text)

    return wav_to_text

# google翻訳:今回追加分;翻訳不要の場合は下記をコメントアウトする
def Translate_ggl(wavtext):
    translator = Translator()
    wavtext2 = translator.translate(wavtext, dest='en', src='ja')
    tr_text = wavtext2.text
    print(tr_text)
    return tr_text

# deepL翻訳:今回追加分;翻訳不要の場合は下記をコメントアウトする
def Translate_dpl(wavtext):
    translator = deepl.Translator(os.getenv("DEEPL_AUTH_KEY")) # DEEPL_AUTH_KEYはDocker Composeに記述している
    result = translator.translate_text(wavtext, target_lang="EN-US")
    print(result)
    return result

# 一応既に同じ名前のディレクトリがないか確認。
out_dir = "output"
file = os.path.exists(out_dir)
# print(file)

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

fTyp = [("","*.wav")]
#iDir = os.path.abspath(os.path.dirname(__file__)) はJupyterlabではエラーになる
iDir = os.path.join(Path().resolve()) #Jupyterlabではこちらでパスを指定する:今回修正分
f_name = tkinter.filedialog.askopenfilename(filetypes = fTyp,initialdir = iDir)

cut_time = 60
out_file = "output/out.xlsx"
cut_wav(f_name,float(cut_time))


Pythonコード修正の解説

追加分

6行目【from pathlib import Path】:音声ファイル読込時のファイルパス選定用

12行目【from googletrans import Translator】:google翻訳用

13行目【import deepl】:deepL翻訳用

14行目【import openpyxl】:Excelファイルを生成するときに必要



修正分

43〜46行目:今回は巻き戻さないので、音声をカットした部分を少し巻き戻すコード以下3行をコメントアウトする。

#if i > 0:
#    start_cut = int(i*frames) - int(180000)
#else:

47行目:巻き戻さないので、インデントを削除。削除しないとエラーになる。逆に、巻き戻し設定する場合はこの行をインデントすること。インデントしないとエラーになる。

start_cut = int(i*frames)


翻訳用追加分(共通)

64行目:翻訳テキストを収納する変数で、初期値を空に設定。

str_tr_out = ""

65行目:翻訳不要時の設定。翻訳不要の場合はこちらを使用すること。

#list1 = [wavf,"",""] # 

66行目:翻訳用。65行目に「,"",""」を追加。翻訳不要時はこちらをコメントアウトすること。

list1 = [wavf,"","","",""]

68行目:Excelの項目名。翻訳不要時の設定。翻訳不要の場合はこちらを使用すること。

#df_x.columns = ['No', '音声ファイル', '変換結果']

69行目:Excelの項目名。翻訳用。68行目に「,'ggl翻訳','dpl翻訳'」を追加。翻訳不要時はこちらをコメントアウトすること。

df_x.columns = ['No','音声ファイル','変換結果','ggl翻訳','dpl翻訳']

76行目:Excelへ収納する内容。翻訳不要時の設定。翻訳不要の場合はこちらを使用すること。

 #df_x.loc[ii] = [ii,str(ii) + '.wav',str_out]

77行目:Excelへ収納する内容。翻訳用。76行目に「,str_ggl_out,str_dpl_out」を追加。翻訳不要時はこちらをコメントアウトすること。

df_x.loc[ii] = [ii,str(ii) + '.wav',str_out,str_ggl_out,str_dpl_out]


翻訳用追加分(google)

74行目:73行目のstr_outをgoogle翻訳する。翻訳不要の場合はコメントアウトすること。

str_ggl_out = Translate_ggl(str_out)

96〜101行目:google翻訳を定義する関数。翻訳不要の場合はコメントアウトすること。

def Translate_ggl(wavtext):
    translator = Translator()
    wavtext2 = translator.translate(wavtext, dest='en', src='ja')
    tr_text = wavtext2.text
    print(tr_text)
    return tr_text

Pythonを使って日本語から多言語への自動翻訳をやってみる。自動翻訳=音声認識+機械翻訳+音声合成

これまでの記事で、Pythonを使って、音声認識、音声合成、機械翻訳のやり方を説明してきました。今回はこれらの応用として、Pythonを自動翻訳(自動音声翻訳)の実装を解説していきたいと思います。

実は、googletransが悪さをして、『AttributeError: 'NoneType' object has no attribute 'group'』というエラーを吐いて全く進まなかった。
調べたところ、googletransは長らく安定していなかったようで、気まぐれに成功したりエラーになったり、ということだった。
googletransの最新バージョンは安定しているということだったので、最新版のgoogletrans==4.0.0-rc1をインストールするようDockerfileを書き換えたところ、上手くいった。

【python】googletransの『AttributeError: 'NoneType' object has no attribute 'group'』対策【2021/01/12追記】 - Qiita

pythonでgoogletransライブラリを使って翻訳していると、ある日 AttributeError: 'NoneType' object has no attribut...



翻訳用追加分(deepL)

自動翻訳では定評のあるdeepLにも、Python用のライブラリが用意されている。
無料版もあるが、deepLのサイトでdeepL API Freeに事前登録し、API認証キーを取得しなければならない

DeepL APIで使えるPythonのクライアントライブラリ

We’re excited to announce the release of our Python client library for the DeepL API.

75行目:73行目のstr_outをdeepL翻訳する。翻訳不要の場合はコメントアウトすること。

str_dpl_out = Translate_dpl(str_out)

104〜108行目:deepL翻訳を定義する関数。翻訳不要の場合はコメントアウトすること。
下記DEEPL_AUTH_KEYはDocker Composeの9行目の認証キーを読み込む。

def Translate_dpl(wavtext):
    translator = deepl.Translator(os.getenv("DEEPL_AUTH_KEY"))
    result = translator.translate_text(wavtext, target_lang="EN-US")
    print(result)
    return result


Jupyterlab用の修正

120〜121行目:文字起こしするwaveファイルを選択できるようにダイアログを表示させるためのもの。
120行目の設定ではJupyterlabではエラーになるのでコメントアウトする。__file__がJupyterlabでは対応していないとのこと。
Jupyterlabに対応した121行目を追加する。

#iDir = os.path.abspath(os.path.dirname(__file__)) 
iDir = os.path.join(Path().resolve())

Jupyter Notebookで__file__が使用できない - 筋肉エンジニアのAIって何なの?

直感ディープラーニングの写経をJupyter Notebookで行っていて、 __file__ が使用できないことがわかった。 以下のように対象ファイルのパス情報を取得できない。 import os os.path.dirname(__file__) 代わりに、以下を使用する。 from pathlib import Path Path().resolve() 直感ディープラーニングではログディレクトリを作成して 学習結果を出力し、TensorBoardで確認を行う。 書籍場では以下のように実行ファイルが含まれているディレクトリ情報を取得し、 その配下にlogdirというディレクトリを作成する…



Docker ImageとContainerの作成

上記のディレクトリの通りにセットし、Terminalを起動して必要なコマンドを打ってDocker ImageとContainerを作成する。
そのあと、xhostの設定やJupyterlabを開くなどあるが、それらの詳細は、以下をご覧あれ

【完成版】MacでJupyterlabからPythonのGUIライブラリが使えるDockerの設定方法

PythonのGUIライブラリ(Tkinter、pillow、openpyxl)をDocker Container内にインストールし、JupyterlabからXQuartzを介して稼働させる方法と、Docker ComposeとDockrfileの作成方法を紹介します。



コードを走らせる

1分ごとに音声を分割して文字起こし

2分強のサンプル音声データを以下からお借りしております。

ブラウザを開き、URL欄に

localhost:8000

と入力しJupyterlabを開き、上記のPythonコードをコピペしてコードを走らせる。

すると、読み込むwavファイルを尋ねてくるので該当するものを選択。

選択後、自動でoutputフォルダが生成し、outputフォルダ内に分割されたwavファイルと文字起こしテキストが収納されたexcelファイルが生成されたら成功

(作成後のディレクトリ画像)
(完成したExcelシート)

音声ファイルを分割せずに文字起こし

2分強なので分割しなくてもいけるかも。

音声ファイルを分割しているのは、googletransの仕様で制限があるためらしい。deepLはどうだろうか?(未調査)

分割の場合、分割箇所を制御出来ないので、文の途中で分割されて翻訳が怪しくなる

Pythonコードの下から3行目を以下の通り修正して3分で分割するように変更し、コードを走らせる。

cut_time = 60*3
(完成したExcelシート)

翻訳ありの場合は、音声分割しない方がよいですね



他の参考サイト

音声データを1/100秒単位で分割する - Qiita

wavファイルを等間隔に分割 【Python】WAVファイルを等間隔に分割するプログラム【サウンドプログラミング】を参考にして以下のコードを書きました...

Pythonで音声からテキストへ変換【SpeechRecognition】

「Pythonでの音声認識に関する検索結果を見て混乱していませんか?」この記事では、Pythonで音声認識をする上での正しい情報を提供しています。この記事を読めば、コピペでよくわからないまま無駄なライブラリをインストールすることもなくなります。










コメント

よく読まれている記事

CSSボタンでテキストを天地中央に揃えるとき、なぜボタン高と行高を一緒にするのか

FullCalendarの導入からカレンダー毎の色指定まで

FacebookページのフィードURLを取得しウォールを自サイトに表示