음성신호처리에서 아주 기본적인 feature로 spectrogram이 존재한다. spectrogram을 많이 쓰지만 왜 짧은 시간으로 나눠서 Fourier transform을 하는 지에 대해 생각하지 않고 쓰는 경우가 많다. Python에서 함수 한 줄로 구할 수 있는 spectrogram의 구현을 먼저 알아보고 후에 그 의미도 알아보자.
STFT 와 Spectrogram python 구현
Spectrogram은 위와 같은 그림으로 표현할 수 있다. x축은 시간 축(단위: frame), y축은 주파수를 의미한다. 그리고 각 시간당 주파수가 가지는 값을 값의 크기에 따라 색으로 표현하여 3차원을 2차원으로 표현하게 된다. 즉, 시간의 흐름을 가지는 푸리에 변환이라고 생각할 수 있다. 각각의 frame(짧은 시간으로 자른 신호)이 각 주파수성분을 얼마만큼 가지고 있는 지 표현하게 된다. STFT의 단순히 magnitude 부분을 취해서 db스케일로만 변환해주면 spectrogram이 된다.
사실 STFT와 spectogram의 실질적인 내용에 대해 잘 모르더라도 음성의 feature를 계산해주는 하나의 도구로서 사용할 수 있다. python에서는 librosa라는 라이브러리를 이용하여 쉽게 STFT와 spectrogram을 계산할 수 있다. 코드를 살펴보며 사용법을 알아보자.
import os
import numpy as np
import librosa
import librosa.display
import matplotlib.pyplot as plt
x = librosa.load('C:/Users/hyu/Desktop/train/train_00000.wav',16000)[0]
y = librosa.stft(x, n_fft=128, hop_length=64, win_length=128)
magnitude = np.abs(y)
log_spectrogram = librosa.amplitude_to_db(magnitude)
plt.figure(figsize=(10,4))
librosa.display.specshow(log_spectrogram, sr=16000, hop_length=64)
plt.xlabel("Time")
plt.ylabel("Frequency")
plt.colorbar(format='%+2.0f dB')
plt.title("Spectrogram (dB)")
stft함수의 파라미터:
- x: input wavfile
- n_fft: fft point
- hop_length: shift size
- win_length: frame length
- window: default: 'hann'
결과창:
일단 입력의 길이는 4초이다. 이를 sampling rate 16kHz를 적용하면 64000sample이 나온다. 그래서 input의 길이는 64000이 된다. 그리고 fft point는 128로 했으니 주파수를 128개로 쪼개는 것이다. 근데 65개의 차원만 가지는 이유는 fft결과는 대칭으로 나와서 뒷 부분은 몰라도 되기 때문이다. 그리고 1001은 frame 갯수를 뜻한다. 그리고 window length는 fft point보다 커지면 오류가 뜨면서 실행이 되지 않는데 이는 앞선 포스팅인 DFT에서 그 이유에 대해 장황히 설명해 두었지만, time sampling이 원인이다. 그렇기 때문에 window length의 경우 fft point보다 크게 하지 말도록 한다.
왜 Short-time analysis를 해야하는가?
그렇다면 그냥 신호 전체를 푸리에변환하면 되지, 왜 굳이 작은 시간단위로 쪼개서 STFT를 할까? 라는 의문이 생길 수 있다. 신호를 deterministic한 관점에서 볼 수도 있지만, 우리가 받는 음성신호의 경우 신호 및 시스템 책에서처럼 식이 정해져있는 신호가 아니므로 stochastic한 관점으로 볼 필요가 있다. (=어떤 식으로 다음 값이 나올지 모르기 때문에, 통계적인 관점에서 신호를 볼 필요가 있다.) 이때 각 sample들이 random variable의 realization이 되는데, x[3]=4으로 값이 나왔다고 한다면, x[3]이 가질 수 있는 많은 값들 중 하나인 4로 realize된 것이다. 그렇게 random variable들을 모으면 random process가 되기 때문에 이 speech signal은 random process로 볼 수 있겠다.
그러니까 이번에 나온 random signal은 여러 시도 중 하나가 실현된 것이다. 만약에 위의 그림처럼 t=t0로 특정시간에 고정된다면, 나올 수 있는 모든 시도에서 random variable t0에 대한 평균을 구할 수 있다. 또한 random variable이기때문에 pdf도 구할 수 있을 것이고, 다른 시간샘플들과의 joint pdf도 구할 수 있을 것이다.
그런데, 우리가 이런 random signal을 가지고 여러 processing을 하고자 하는데 음성을 들어보면, noise-like한 excitation을 갖는 무성음이 되기도 하고, 주기를 가지고 진동하는 유성음이 되기도 한다. 이렇듯 음성의 특성들이 시간에 따라 변하게 된다. 이런 long-term signal이 시간에 따라서 통계적 특성이 자꾸 변한다면(=non-stationary하다면) processing을 할 수 없다.
그렇다고해서 통계적 특성이 샘플단위로 급격하게 변화하지는 않는다. 그래서 우리는" speech signal의 특성이 상대적으로 느리게 변한다고 가정하고, 짧은 시간간격 안에서는 stationary하다"고 가정한다. 그렇기 때문에!!! 짧은 시간간격으로 나누는 framing과정을 거쳐서 short-time에 대한 processing을 하게된다.
Framing & Windowing
그렇다면 이제 signal을 어떻게 short-time signal로 자를 것이냐에 대한 고민을 해야한다. 그냥 댕강댕강 짜르면 되지 않을까 하지만 실은 그렇지 않다. 이제 window라는 개념을 통해서 어떻게 short-time signal로 만드는지 알아보겠다. 댕강댕강 짜르는 방식은 rectangular window를 취하는 것으로 구현할 수 있지만, 실제로 이를 fourier transform해보면 sinc함수가 나오기때문에 주파수상에서 무한한 값을 가져야한다. 이는 굉장히 구현하기 어렵고 그렇다고 막 좋지도 않다. (spectral leakage가 많다.) 대체로 hamming window를 많이 쓰는 편이다. signal에 window를 취해 short-time signal(frame)을 만드는 과정은 아래의 식과 같다.
이를 그려보면 훨씬 이해가 잘 되므로 그림을 통해 설명하겠다.
이렇게 윈도우를 y축에 대해 뒤집고 우리가 분석하려는 frame으로 m만큼 shift해주면 된다. m이 이제 shift하는 time step이 되겠고, window length가 frame길이가 되겠다. 그리고 또 한가지 window에서 보고 넘어가야하는 점은 우리가 다루는 윈도우들이 symmetric하다는 점이다. 이는 우리가 윈도우에서 얻고자하는 특성이 반영된 것인데, 이 특성은 linear phase이다. 윈도우를 취한다고해서, 신호의 phase특성이 손상받으면 안될 것이다. 그렇기 때문에 linear phase가 되도록 윈도우를 설계하게된다.
(윈도우의 종류나 특성등은 시간이 나면 추가하도록 하겠다...)
Window의 길이는 어떻게 정해야 할까?
window의 길이는 사실 application에 따라서 달라질 수 있다. 만약에 frequency resolution을 증가시키길 원한다면 더 긴 window를 사용할 수 있고, time resolution을 증가시키길 원한다면 더 짧은 winow를 사용할 수 있다.
대체로는 20~30msec의 hamming window를 많이 사용한다. 그리고 speech의 non-stationarity를 모델링하기 위해서 10msec(윈도우 length보다 작게설정) overlap을 할 수 있다. (overlap하는이유)만약, 16kHz samping rate를 갖을 때, 20msec window는 320samples를 가지게된다.
- frame size: window의 길이
- frame rate: 1/frame_size (ex. 10msec 윈도우라면 frame rate는 100Hz)
Short-Time Signal Processing의 전체과정
STFT에 대해 자세히 알아봤으니 Short-time signal processing의 전반적인 과정도 살펴보도록 하겠다. 이 부분은 classification task나 음성인식 분야에서는 간과할 수 있다. 왜냐하면 음성을 통해 도출하고자 하는 지식이 다시 음성 domain이 아니기 때문이다. 하지만 음성향상이나 음성분리등의 분야에서는 이 과정이 꼭 필요하다.
*STFA: Short-Time Fourier Analysis
*STFS: Short-Time Fourier Synthesis
STFT를 통해 얻은 spectrogram과 side information인 pitch, formant, VAD label등을 이용해서 signal에 어떤 처리를 가할 것이다. 예를들어 음성향상이라면 noise를 제거하는 처리를 할 것이다. 처리가 끝나면 끝인가? 아니다. 여전히 이 신호는 frequency domain일 것이고 이를 다시 time domain으로 synthesis해서 clean speech를 뽑아내는 것까지 Short-Time Signal Processing의 과정이다. 그렇다면 어떻게 조각조각 잘린 신호를 다시 synthesis할까?
STFS: Overlap-add를 통한 synthesis
간단한 기법으로 overlap-add가 존재한다. 쪼개져있는 시간 frame을 overlap하여 더하는 기법이다. 가장 많이 사용하는 기법이라고 할 수 있겠다. 아래의 그림과 같이 inverse FFT를 거친 각 frame들은 overlap하여 더해주면 다시 time domain으로 synthesis할 수 있다.
Reference
Lawrence R. Rabiner “Theory and Applications of Digital Speech Processing”
'Domain Knowledge > Speech' 카테고리의 다른 글
LPC(Linear Prediction Coding, 선형예측부호화)와 formant estimation (0) | 2020.07.31 |
---|---|
Pitch detction(ACF, AMDF, Cepstrum) (0) | 2020.07.31 |
Speech production and perception(음성의 생성과 인지) (0) | 2020.07.31 |
음성신호처리에서 frame 생성시 overlap을 하는 이유 (5) | 2020.07.30 |
DFT(Discrete Fourier Transform)와 Circular Convolution (2) | 2020.07.07 |