- レビュー
PDFからテキストに変換する
PDFからテキストを抽出する方法はいくつかある。ここでは、popplerのpdftotextを使う例を紹介する。
popplerをインストールする。
brew install poppler
pdftotextを使ってテキストを抽出する。
pdftotext example.pdf
i18n用POTファイルを作成する
pip install polib
import polib
po = polib.POFile()
entry = polib.POEntry(
msgid="Hello world",
msgstr="",
)
po.append(entry)
po.save("output.pot")
翻訳機の精度比較
本を読む
pdftotext -f 35 -l 35 foo.pdf - | python pdfreader.py
本を読む続き
pdftotext -f 35 -l 35 foo.pdf - | python pdfreader.py
sounddeviceからデータを取得しwavファイルとして出力する
import time
import wave
import numpy as np
import sounddevice
SAMPLE_RATE = 16000
CHANNELS = 1
WINDOW_SIZE_SAMPLES = 512
wav_file = wave.open("output.wav", 'wb')
wav_file.setnchannels(CHANNELS) # チャネル数 (モノラル)
wav_file.setsampwidth(2) # サンプル幅 (2バイト)
wav_file.setframerate(SAMPLE_RATE)
def callback_sound(indata: np.ndarray[np.ndarray[np.int16]],
outdata, frames, time, status):
if status:
print(status)
# インプットデータを書き込み
wav_file.writeframes(indata.copy())
try:
with sounddevice.Stream(samplerate=SAMPLE_RATE, dtype="int16",
channels=CHANNELS,
blocksize=WINDOW_SIZE_SAMPLES,
callback=callback_sound):
while True:
time.sleep(0.01)
except KeyboardInterrupt:
print("録音終了")
finally:
wav_file.close()
ffmpegで音声のノイズを除去する
ffmpegにノイズを除去するフィルターがある事を知った1。そこで、これを使ってみる事にした。
ffmpeg -i INPUT_FILE.wav -af 'afftdn=nr=10' OUTPUT_FILE.wav -y
afftdn2は、STFT3を使用し周波数特性の差によりノイズを除去する。いくつかのパラメーターを渡す事で挙動を調整できる。
param | 例 | 意味 | 説明 |
---|---|---|---|
n | 16 | ノイズプロファイル | 特定のノイズプロファイルを指定する。 |
o | 0.75 | オーバーラップ | 高くすることで、スムーズなノイズ除去となる。 |
w | 1024 | ウィンドウサイズ | STFTのウィンドウサイズを指定する。通常、指定する必要はない。 |
sn | 1 | 間引き | ノイズプロファイルのスケール。0: 間引きしない、1: 間引きする |
自前で音声ノイズ除去を実装してみる
未完成。ノイズ除去どころか、ノイズだらけになった。なんでだ…
import time
import wave
import librosa
import numpy as np
import sounddevice as sd
SAMPLE_RATE = 16000
CHANNELS = 1
NOISE_PROFILE_DURATION = SAMPLE_RATE # 最初の1秒間をノイズプロファイルに使用
WINDOW_SIZE_SAMPLES = 512
print("ノイズ除去のため環境音を取得します。声を出さないでください。")
noise_audio = sd.rec(NOISE_PROFILE_DURATION,
samplerate=SAMPLE_RATE,
channels=CHANNELS, dtype='float32')
sd.wait()
print("ノイズ除去のため環境音を取得しました。")
# ノイズプロファイルのSTFTを計算
noise_profile_data = noise_audio[:, 0]
noise_stft = librosa.stft(noise_profile_data, n_fft=WINDOW_SIZE_SAMPLES, hop_length=WINDOW_SIZE_SAMPLES//2)
magnitude_noise, _ = librosa.magphase(noise_stft)
wav_file = wave.open("output.wav", 'wb')
wav_file.setnchannels(CHANNELS) # チャネル数(モノラル)
wav_file.setsampwidth(2) # サンプル幅(2バイト)
wav_file.setframerate(SAMPLE_RATE)
def callback_sound(indata: np.ndarray, outdata, frames, time, status):
global magnitude_noise
if status:
print(status)
# ノイズリダクションを適用
y = indata[:, 0].astype('float32')
y_stft = librosa.stft(y, n_fft=WINDOW_SIZE_SAMPLES, hop_length=WINDOW_SIZE_SAMPLES//2)
magnitude_y, phase_y = librosa.magphase(y_stft)
# 長さを調整
if magnitude_y.shape[1] > magnitude_noise.shape[1]:
magnitude_noise = np.pad(magnitude_noise, ((0, 0), (0, magnitude_y.shape[1] - magnitude_noise.shape[1])), 'constant')
elif magnitude_noise.shape[1] > magnitude_y.shape[1]:
magnitude_noise = magnitude_noise[:, :magnitude_y.shape[1]]
magnitude_reduced = np.maximum(magnitude_y - magnitude_noise, 0.0)
reduced_noise_stft = magnitude_reduced * phase_y
reduced_noise_data = librosa.istft(reduced_noise_stft, hop_length=WINDOW_SIZE_SAMPLES//2)
data = (reduced_noise_data * 32767).astype(np.int16).tobytes()
wav_file.writeframes(data)
try:
with sd.Stream(samplerate=SAMPLE_RATE, dtype="int16",
channels=CHANNELS, blocksize=WINDOW_SIZE_SAMPLES,
callback=callback_sound):
print("録音を開始します。 Ctrl+C で停止できます。")
while True:
time.sleep(0.01)
except KeyboardInterrupt:
print("終了")
finally:
# WAVファイルを閉じる
wav_file.close()
文字起こし
スピーチ to スピーチの作業の続き。途中までの処理は基本的に文字起こしと同じになる。
import time
import sys
import sounddevice
import numpy as np
import torch
from reazonspeech.nemo.asr.audio import audio_from_tensor
from nemo.collections.asr.models import EncDecRNNTBPEModel
from reazonspeech.nemo.asr.transcribe import transcribe
torch.set_num_threads(1)
THREASHOLD = 0.5 # 発話検出の閾値 (ex: 0.5)
SAMPLE_RATE = 16000
SAMPLING_RATE = 16000
WINDOW_SIZE_SAMPLES = 512 # 512 if SAMPLING_RATE == 16000 else 256
model, utils = torch.hub.load("snakers4/silero-vad", "silero_vad")
rez_model = EncDecRNNTBPEModel.from_pretrained(
'reazon-research/reazonspeech-nemo-v2',
map_location="cpu")
(get_speech_timestamps,
save_audio,
read_audio,
VADIterator,
collect_chunks) = utils
model.reset_states()
speech_intervals = []
current_start = None
speech_data = None
speech = None
chunk = None
def callback_sound(indata: np.ndarray[np.ndarray[np.int16]],
outdata, frames, time, status):
global speech
audio_chunk = indata
audio_int16 = np.frombuffer(audio_chunk, dtype=np.int16)
abs_max = np.abs(audio_int16).max()
sound = audio_int16.astype('float32')
if abs_max > 0:
sound *= 1/32768
audio_float32 = sound.squeeze()
x = chunk = torch.Tensor(audio_float32)
# chunk: torch.Tensor = torch.from_numpy( # torch.Tensor torch.float32
# indata.astype(np.float32))
window_size_samples = len(x[0]) if x.dim() == 2 else len(x)
speech_prob = model(chunk, SAMPLING_RATE).item()
# print(speech_prob)
if speech_prob > THREASHOLD: # 発話検出
if speech is None:
speech = chunk
else:
speech = torch.cat((speech, chunk), dim=0)
return
elif speech is not None:
print(speech)
if speech.shape[0] >= WINDOW_SIZE_SAMPLES :
audio = audio_from_tensor(speech, SAMPLING_RATE)
ret = transcribe(rez_model, audio)
for text in ret.segments:
print(text, end="")
print("\n")
speech = None
with sounddevice.Stream(samplerate=SAMPLE_RATE, dtype="int16",
channels=1, blocksize=WINDOW_SIZE_SAMPLES,
callback=callback_sound):
while True:
time.sleep(0.01)
フーリエ解析
フーリエ解析とは、現象を三角関数に分解して考える。