GoogleSpeechAPIにより.wavファイルの文字起こしを行うPythonスクリプトプログラム
文字起こしレベルの議事録を作れという要求に対応し、かつ残業しないための道具
- 必要なライブラリのimport
import wave
import struct
from scipy import int16
import numpy as np
import os
import math
import pandas as pd
import speech_recognition as sr
import tkinter.filedialog
- wav_to_text関数は単純に入力された音声ファイルをGoogleSpeechAPIで解析するもの。
def wav_to_text(wavfile):
r = sr.Recognizer()
with sr.AudioFile(wavfile) as source:
audio = r.record(source)
text = r.recognize_google(audio,language='ja-JP')
print(os.path.abspath(wavfile))
print(text)
return text
- cut_wav関数は、GoogleSpeechAPIが長すぎる音声ファイルを拒否するため、事前にファイルを細切れにしてからwav_to_text関数で解析処理を行いcsvへ保存するもの
パラメータの説明
def cut_wav(filename,save_file_name,time):
#細切れにしたファイルを保存するためのディレクトリを指定しているため、宜しく環境に合わせて’’内を書き換えること
out_dir = os.path.abspath('hogehoge')
with wave.open(filename, mode='rb') as wr:
#waveライブラリはPythonの標準ライブラリであるため、Documentを読めばおおよそのことが理解できる。
ch = wr.getnchannels()
width = wr.getsampwidth()
fr = wr.getframerate()
fn = wr.getnframes()
total_time = fn / fr
integer = math.floor(total_time*100) #math.floor(x):xの底(x以下の最大の整数値)
t = int(time*100)
frames = int(ch * fr * t/100)
num_cut = int(integer//t) #切り捨て除算
data = wr.readframes(fn)
X = np.frombuffer(data,dtype=int16) #numpyでのメモリから直接読み込むメソッド。音声ファイルをndarrayに入れるする場合はこのメソッドが高速化できるものになる。
for i in range(num_cut + 1):
outf = os.path.join(out_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)
Y = X[start_cut:end_cut]
outd = struct.pack('h' * len(Y),*Y)
with wave.open(outf,mode='wb') as ww:
ww.setnchannels(ch)
ww.setsampwidth(width)
ww.setframerate(fr)
ww.writeframes(outd)
list1 = [filename,'','']
df = pd.DataFrame([list1])
df.columns = ['no','音声ファイル','変換結果']
for ii in range(num_cut + 1):
# 保存した細切れの音声ファイルを順番に解析にかけている処理である。
outf = os.path.join(out_dir) + '/' + str(ii) + '.wav'
str_out = wav_to_text(outf)
df.loc[ii] = [ii,str(ii)+'.wav',str_out]
df.to_csv(save_file_name)
- tkinterライブラリにより保存するファイル名と読み込む対象の音声ファイルをダイアログで聞き、処理を行う内容を記載している。
file_type = [("","*.wav")]
i_dir = os.path.abspath('fugafuga')
f_name = tkinter.filedialog.askopenfilename(title='Please Select Targert a .wav file',filetypes = file_type,initialdir = i_dir)
save_target_file = tkinter.filedialog.asksaveasfilename(title='Please Input Save FileName',initialdir= i_dir)
time = 30
cut_wav(f_name,save_target_file,float(time))
pythonでPDFを操作する場合には、PyMuPDFがいいんでない?という話
pythonでPDFを操作したい
python(django)で会議用の管理システムを開発したのですが、最も実装したかった機能が資料のPDFを結合したり、通し番号を入れたりする機能でした。
pythonのPDF用ライブラリを検索すると、よく出てくるのがPyPDF2やpdfrwですが、今回のシステムで使用したのがPyMuPDFです。
当初はPyPDF2、pdfrwを使用したコードを書いたのですが、PDFに組み込まれているJavaScriptがどうしても処理上の邪魔になったりで思うようにバグが消せなかったところで、そもそも各々のPDFファイルに組み込まれたJavaScriptをはじめに削除することができるPyMuPDFを使用することで一気に問題が解消しました。
PDFを操作する際の問題点
今回実装したPDFの操作機能には、主に以下の前提条件が与えられます。
- 資料となるPDFはあらゆる人間があらゆるソフト(またはスキャン)で作成しアップロードしてくる。
- 資料のPDFは様々な大きさ、アスペクトが与えられている。
- さらに、JavaScriptによって印刷条件や表示時の回転処理等が与えられている。
上記の前提条件を踏まえたうえで、任意のPDFファイルを正しく結合させる必要があります。
PyPDF2やpdfrwでの実装
初めにPyPDF2とpdfrwを使用する実装を検討します。この方法ではどちらか一方のライブラリで完結することがまずできません。ネット上を検索すると出てくる方法としては、
- PyPDF2で各PDFファイルを結合する。
- 結合したPDFファイルを再度読み込み、pdfrwのcanvasで各ページのmediaboxと同一の空白ページを1枚ずつ順番に生成し、もともとの該当ページの内容を転記、さらに追記したい内容(今回は通しページ番号)をさらに追記する工程をループしていく。
という方法です。
この方法は規格が決まったPDFファイルではうまくいきますが、回転が加わっていたり、印刷用のJavaScriptが組み込まれているようなPDFファイルではうまくできません。(たとえば、回転角を事前に取得してページ番号を挿入位置や文字の回転角度を逆算して場合分けしてやる必要性があったりコードも煩雑になります。)
当然、様々なPDFファイルが混在する環境では望んだ結果が得られません。
.scrub()を使用してPDFの処理前に事前にJavaScriptを削除する。
上記の問題はほとんどがJavaScriptが望んでいない挙動を起こすことから生まれますので、とにかく対象とするPDFを結合する処理を行う前に、余計な情報を削除してやる必要がありますが、PyMuPDFでは
import fitz idoc = fitz.open(file_name) idoc.scrub()
基本的にこれだけで事足ります。.scrub()で余計なJavaScriptを削除したら、あとは公式ドキュメントどおりに.insertText()メソッドで
target_x, target_y = media_wsize/2,media_hsize-20 p = fitz.Point(target_x,target_y) rc = read_odoc.insertText( p, # bottom-left of 1st char insert_text, # the text (honors '\n') fontname="helv", # the default font fontsize=14, # the default font size rotate=0, # also available: 90, 180, 270 color=(0,0,0), )
書き足したい文字を挿入するだけです。
PyMuPDFのドキュメントについて
PyMuPDFの情報については日本語ではほとんど得られなかったので、公式ドキュメントを読むのが一番いいと思います。
GoldenCheetah OpenDataを使って到達パワーを回帰予測してみる
ちょっと真面目にトレーニングをしているサイクリストなら、一度は使ったことがあるであろう「Golden Cheetah」プロジェクトが公開しているトレーニーのオープンデータを使用して、トレーニングによって期待できる最大パワーを予測してみました。という話です。
GoldenCheetah OpenDataとは
まぁ、詳しくは私も知らないので、(今回も結構趣味の遊びの範囲を出ませんし)githubを読んでみてほしいのですが、goldencheetahを使用しているトーレーニーの人たち(サイクリストだけでなく、トライアスリートも多めです。)のトレーニングデータがcsv形式で公開されているものです。 データは、各アクティビティデータもありますし、トレーニーごとのクリティカルパワー、体重なんかのプロフィールデータもあります。それぞれは個別IDで付番されているので、データの結合も容易でした。
20分間のクリティカルパワーを予測する回帰モデル
一昔前であれば、ローラー台でやるメニューと言えば20分走でした。個人的に興味のあるこの領域を予測できるモデルを目指します。 使用したデータは、GC OpenDataのathletes.csvです。このデータに入っているカラムはこんな感じでした。
import pandas as pd athletes = pd.read_csv("athletes.csv") print(athletes.columns) Index(['id', 'age', 'gender', 'activities', 'bike', 'run', 'swim', 'other', '1s_critical_power', '15s_critical_power', '2m_critical_power', '3m_critical_power', '5m_critical_power', '8m_critical_power', '10m_critical_power', '20m_critical_power', '30m_critical_power', '1m_peak_wpk', '5m_peak_wpk', '10m_peak_wpk', '20m_peak_wpk', '30m_peak_wpk', '20s_peak', '20s_peak_wpk', '60s_peak', '60s_peak_wpk', '180s_peak', '180s_peak_wpk', '240s_peak', '240s_peak_wpk', '420s_peak', '420s_peak_wpk', '720s_peak', '720s_peak_wpk', 'weightkg', ' weightstd'], dtype='object')
***_critical_power 以降のデータはラン、スイムの指標なので、今回はあまり使わなくても良さそうです。逆に"weightkg"や"age"は経験則上かなり相関がありそうですが、各変数の寄与具合も気になるところです。
ここから、このデータをクレンジングする作業が続くわけですが、あくまでも個人的興味に基づく分析なので、年齢は18歳から60歳、体重は40kgから100kg、パワーも大きな外れ値(2000Wとか)は.loc[]で外していきました。 そんなこんなで解析にかけるデータ(具材)をちまちま用意して、scikit-learnで回帰予測をしてみます。
from sklearn import linear_model import sklearn.model_selection model = linear_model.LinearRegression() X = predict_data_rules[["age","bike","gender","5m_critical_power","10m_critical_power","weightkg"]] y = predict_data_rules["20m_critical_power"] X_train,X_test,y_train,y_test = sklearn.model_selection.train_test_split(X,y) model.fit(X_train,y_train)
rider = [30,600,1,300,300,70] #年齢、練習回数、男女、5分最大パワー、10分最大パワー、体重から、20分最大パワーを回帰予測する pred = [rider] print("期待できる20分最大パワー:", model.predict(pred).round(2)) 期待できる20分最大パワー: [292.89]
10分のクリティカルパワーの寄与が大きすぎるので、月の練習時間や距離なんかも加えるともっと良いモデルになると思います。
それにしても、この設定値はおおよそ私のベストな数値を入れてみたのですが、出力された期待できるパワーが292Wというのは、確かにこれまで調子いいときに出せてたくらいの数値です。
もう少し解析するデータを整えれば、トレーニングを行う上での改善指標が明確にできるかもしれませんね。