Skip to content

7 lpc and lpcc feature extraction #13

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Mar 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions chapter/Grundlagen.tex
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ \section{Grundlagen}\label{sec:Grundlagen}
Der Schwerpunkt dieser Arbeit unterteilt sich in zwei Teile, die Signalvorverarbeitung und das \ac{LPC} Verfahren.
Im Folgenden werden die theoretischen Grundlagen für beide Prozesse beschrieben.

\subsection{Signalvorverarbeitung}
\subsection{Signalvorverarbeitung}\label{sec:Signalvorverarbeitung}
Um ein gegebenes Audiosignal einheitlich verarbeiten zu können, muss dieses zunächst mittels verschiedener Verfahren vorbereitet werden.
Ziel dieser Vorverarbeitung ist es, die Effizienz und Effektivität des anschließenden Verarbeitungsprozesses zu erhöhen und somit ein verbessertes Ergebnis zu erzielen \autocite[vgl.][S. 11672]{lokesh_speech_2019}.
Die Vorverarbeitung im Rahmen dieser Arbeit beinhaltet die vier Schritte Rauschreduzierung, Pausen entfernen, Framing und Windowing, welche in den folgenden Unterkapiteln genauer erläutert werden.

\subsubsection{Rauschreduzierung}
\subsubsection{Rauschreduzierung}\label{sec:Rauschreduzierung}
Um störende Frequenzen aus dem Audiosignal zu entfernen wird eine Rauschreduzierungsfunktion verwendet.
Die in dieser Arbeit verwendete Funktion nutzt den sogenannten Spectral Noise Gate Algorithmus.
Dabei wird zunächst die Signatur des Rauschens ermittelt.
Expand Down
26 changes: 25 additions & 1 deletion chapter/TechnischeUmsetzung.tex
Original file line number Diff line number Diff line change
@@ -1 +1,25 @@
\section{Technische Umsetzung}\label{sec:TechnischeUmsetzung}
\section{Technische Umsetzung}\label{sec:TechnischeUmsetzung}
% TODO: Optional: Quellen hinzufügen wenn Platz vorhanden ist.
Da die Zuordnung der erzeugten \ac{LPC} Koeffizienten zu einem spezifischen Sprecher mittels eines \acp{NN} umgesetzt wird, wird auf die Programmiersprache Python zurückgegriffen.
Diese ermöglicht die Verwendung des von Google entwickelten Machine Learning Frameworks TensorFlow.
Folglich findet auch die Implementierung der Signalvorverarbeitung, sowie der \ac{LPC} Berechnung mit Hilfe der Sprache Python statt.

Um Programmierfehler zu vermeiden, sowie die Effizienz des Codes zu erhöhen, werden Funktionen aus verschiedenen Bibliotheken verwendet.
Als Basis wird die Bibliothek \textKlasse{numpy} verwendet, welche Funktionen für die Bearbeitung von Arrays und Matrizen bereitstellt, sowie die Bibliothek \textKlasse{librosa} für Audio spezifische Funktionen wie das Laden von WAV Dateien.

\subsection{Klasse AudioPreprocessor}
Die Klasse \textKlasse{AudioPreprocessor} (vgl. Quellcode~\ref{code:AudioPreprocessor}) beinhaltet die Funktionen für die Schritte der Signalvorverarbeitung (vgl. Kapitel~\ref{sec:Signalvorverarbeitung}).
Die Funktion \textFunktion{remove\_noise} implementiert die Rauschreduzierung unter Verwendung der Bibliothek \textKlasse{noisereduce}.
Für die Funktion \textFunktion{remove\_silence} wurde wie bereits erwähnt ein eigener Algorithmus entwickelt (vgl. Zeile~\ref{line:removeSilenceStart}-\ref{line:removeSilenceEnd}), der in Kapitel~\ref{sec:Rauschreduzierung} genauer erläutert ist.
Die Abschließende Unterteilung des Audiosignals in Frames, sowie das Windowing der Frames findet mit Hilfe von \textKlasse{numpy} Operationen in den Funktionen \textFunktion{create\_frames} und \textFunktion{window\_frames} statt.
Die passende Fensterfunktion wird dabei ebenfalls durch die \textKlasse{numpy} Bibliothek bereitgestellt (vgl. Zeile~\ref{line:windowFunction}).

\subsection{Klasse FeatureExtractor}
Mit Blick auf die an diese Arbeit folgende Studienarbeit wird für die Implementierung der Koeffizientenberechnung ein Ansatz gewählt, der eine einfache Erweiterung des Programms um verschiedene andere Verfahren wie etwa \ac{MFCC} ermöglicht.
Dazu wird das Design Pattern Strategie in abgewandelter Form verwendet, wobei zunächst ein Interface für die Berechnungsverfahren erstellt werden muss (vgl. Quellcode~\ref{code:ExtractorInterface}).
Dieses definiert die Funktion \textFunktion{calculateFeatures}, welche in den abgeleiteten Klassen implementiert wird.
Die Klasse \textKlasse{LPCExtractor} (vgl. Quellcode~\ref{code:LPCExtractor}) nutzt hierfür die von der Bibliothek \textKlasse{librosa} bereitgestellt Funktion \textFunktion{lpc} um für die übergebenen Frames die zugehörigen \ac{LPC} Koeffizienten zu berechnen und anschließend zurückzugeben.
Der \ac{LPC} Koeffizienten nullter Ordnung wird dabei von der Funktion standardmäßig mit der Zahl eins befüllt und ist kein Teil der berechneten \ac{LPC}-Ordnung, weshalb dieser manuell entfernt werden muss (vgl. Z.~\ref{line:removeLPC0}).

Die Klasse \textKlasse{FeatureExtractor} (vgl. Quellcode~\ref{code:FeatureExtractor}) implementiert die Funktion \textFunktion{ex\-tract\-\_features}, welcher über den Parameter \textVariable{feature\_list} eine genaue Anweisung über die zu berechnenden Koeffizienten übergeben werden kann (vgl. Z.~\ref{line:extract_features}).
Dabei kann im Speziellen eine Angabe zu der Art der Koeffizienten, der Anzahl an zu berechnenden Koeffizienten, sowie der zusätzlich zu berechnenden Ableitungs-Ordnungen übergeben werden (vgl. Z.~\ref{line:feature_list_info}).
2 changes: 1 addition & 1 deletion chapter/Validierung.tex
Original file line number Diff line number Diff line change
@@ -1 +1 @@
\section{Validierung}\label{sec:Validierung}
\section{Validierung}\label{sec:Validierung}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from utils.utils import Utils
import librosa
import numpy as np
import noisereduce as nr

Expand Down Expand Up @@ -57,7 +57,14 @@ def float_to_int(array, type=np.int16, divide_max_abs=True):
return array

@staticmethod
def remove_silence(y):
def remove_noise(y, sr):
# prop_decrease 0.8 only reduces noise by 0.8 -> sound quality is better than at 1.0
y_ = nr.reduce_noise(y=y, sr=sr, prop_decrease=0.8)

return y_

@staticmethod
def remove_silence(y): #(*@\label{line:removeSilenceStart}@*)
threshold = 0.005
pause_length_in_ms = 200
keep_at_start_and_end = 50
Expand All @@ -79,14 +86,7 @@ def remove_silence(y):

y_ = np.delete(y, indices_to_remove)

return y_

@staticmethod
def remove_noise(y, sr):
# prop_decrease 0.8 only reduces noise by 0.8 -> sound quality is better than at 1.0
y_ = nr.reduce_noise(y=y, sr=sr, prop_decrease=0.8)

return y_
return y_ #(*@\label{line:removeSilenceEnd}@*)

@staticmethod
def create_frames(y, frame_size, overlap):
Expand All @@ -104,7 +104,7 @@ def create_frames(y, frame_size, overlap):
return frames

@staticmethod
def window_frames(frames, window_function=np.hanning):
def window_frames(frames, window_function=np.hanning): #(*@\label{line:windowFunction}@*)
windowed_frames = []

for frame in frames:
Expand All @@ -118,7 +118,7 @@ def load_preprocessed_frames(filepath=None, y=None, sr=None):
raise ValueError("Either filepath or y and sr must be given.")

if y is None or sr is None:
y, sr = Utils.load_file(filepath)
y, sr = librosa.load(filepath)

y = AudioPreprocessor.remove_noise(y=y, sr=sr)
y = AudioPreprocessor.remove_silence(y=y)
Expand Down
Binary file not shown.
3 changes: 3 additions & 0 deletions code/FeatureExtractor/ExtractorInterface.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class ExtractorInterface:
def calculateFeatures(self, frames, sr, order):
pass
47 changes: 47 additions & 0 deletions code/FeatureExtractor/FeatureExtractor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from FeatureExtractor.LPCExtractor import LPCExtractor

import librosa
import numpy as np
from enum import Enum

class Feature(Enum):
LPC = 0

class FeatureExtractor:
def __init__(self, frames, sr):
self.frames = frames
self.sr = sr
self.extractors = [
LPCExtractor()
]
self.last_feature_count = 0

def extract_features(self, feature_list): #(*@\label{line:extract_features}@*)
"""_summary_

Args:
feature_list ((Feature, int, int[])[]): 2D List of Features (enum) + order (int) + deltas (int[]) lists to extract #(*@\label{line:feature_list_info}@*)

Returns:
NDArray[]: Array of requested features for each frame
"""
feature_set = None

for feature_info in feature_list:
features = self.extractors[feature_info[0].value].calculateFeatures(self.frames, self.sr, feature_info[1])
if feature_set is None:
feature_set = np.array(features)
else:
np.concatenate((feature_set, np.array(features)), axis=1)

for delta in feature_info[2]:
delta_features = librosa.feature.delta(np.array(features), order=delta, mode='nearest')
np.concatenate((feature_set, delta_features), axis=1)

feature_set = feature_set.tolist()
self.last_feature_count = len(feature_set[0])

return feature_set

def get_last_feature_count(self):
return self.last_feature_count
12 changes: 12 additions & 0 deletions code/FeatureExtractor/LPCExtractor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from FeatureExtractor.ExtractorInterface import ExtractorInterface

import librosa

class LPCExtractor(ExtractorInterface):
def calculateFeatures(self, frames, sr, order):
lpc_coefficients = []

for frame in frames:
lpc_coefficients.append(librosa.lpc(y=frame, order=order)[1:]) #(*@\label{line:removeLPC0}@*)

return lpc_coefficients
32 changes: 29 additions & 3 deletions code/main.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,34 @@
from preprocessing.AudioPreprocessor import AudioPreprocessor
from AudioPreprocessor.AudioPreprocessor import AudioPreprocessor
from FeatureExtractor.FeatureExtractor import FeatureExtractor, Feature

import librosa
import numpy as np

def unison_shuffled_copies(a, b):
assert len(a) == len(b)
p = np.random.permutation(len(a))
return a[p], b[p]

def main():
frames = AudioPreprocessor.load_preprocessed_frames("./audio.wav")
print(frames)
filePath = "C:\\Users\\SCU8BH\\Documents\\T3000\\Studienarbeit\\Data\\50_speakers_audio_data\\Speaker_0003\\Speaker_0003_00000.wav"

# Load audio file
y, sr = librosa.load(filePath)

# Preprocess audio file
y = AudioPreprocessor.remove_noise(y=y, sr=sr)
y = AudioPreprocessor.remove_silence(y=y)
# frame-duration: 0.2 s, overlap: 0.1 s
frames = AudioPreprocessor.create_frames(y=y, frame_size=int(sr / 5), overlap=int(sr / 10))
windowed_frames = AudioPreprocessor.window_frames(frames=frames)

# Extract features
feature_extractor = FeatureExtractor(windowed_frames, sr)
# Create LPC features with 13 coefficients per frame and no derivatives
extraction_pattern = [
[Feature.LPC, 13, []]
]
features = feature_extractor.extract_features(extraction_pattern)

if __name__ == "__main__":
main()
Loading