diff --git a/bob/pad/face/extractor/FFTFeatures.py b/bob/pad/face/extractor/FFTFeatures.py index 889db74f6c32f5b115aff3089d2cdcb7b7574afe..48148832b600ee76d2b5e511e67edd6c0662bccf 100644 --- a/bob/pad/face/extractor/FFTFeatures.py +++ b/bob/pad/face/extractor/FFTFeatures.py @@ -26,12 +26,13 @@ class FFTFeatures(Extractor, object): debug: boolean Plot stuff """ - def __init__(self, framerate=25, nfft=256, debug=False, **kwargs): + def __init__(self, framerate=25, nfft=256, concat=False, debug=False, **kwargs): super(FFTFeatures, self).__init__(**kwargs) self.framerate = framerate self.nfft = nfft + self.concat = concat self.debug = debug @@ -72,7 +73,11 @@ class FFTFeatures(Extractor, object): ffts = numpy.zeros((3, output_dim)) for i in range(3): ffts[i] = abs(numpy.fft.rfft(signal[:, i], n=self.nfft)) - fft = ffts[1] + + if self.concat: + fft = numpy.concatenate([ffts[0], ffts[1], ffts[2]]) + else: + fft = ffts[1] if self.debug: from matplotlib import pyplot diff --git a/bob/pad/face/extractor/FreqFeatures.py b/bob/pad/face/extractor/FreqFeatures.py index 58e202d2bbbd10d8fe8273caaffee757ef7254a3..4c34996c73bcfac63e72f5f0ff76417540ad55cd 100644 --- a/bob/pad/face/extractor/FreqFeatures.py +++ b/bob/pad/face/extractor/FreqFeatures.py @@ -8,7 +8,6 @@ from bob.bio.base.extractor import Extractor import logging logger = logging.getLogger("bob.pad.face") -from scipy.signal import welch class FreqFeatures(Extractor, object): diff --git a/bob/pad/face/extractor/LTSS.py b/bob/pad/face/extractor/LTSS.py new file mode 100644 index 0000000000000000000000000000000000000000..5637b5d908f8906a7557dc2dc9d13445d25bdb93 --- /dev/null +++ b/bob/pad/face/extractor/LTSS.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python +# encoding: utf-8 + +import numpy + +from bob.bio.base.extractor import Extractor + +import logging +logger = logging.getLogger("bob.pad.face") + +from scipy.fftpack import rfft + + +class LTSS(Extractor, object): + """ + Compute Long-term spectral statistics of a pulse signal. + + The features are described in the following article: + + @Article { + Author = {Muckenhirn, Hannah and Korshunov, Pavel and + Magimai-Doss, Mathew and Marcel, Sebastien } + Title = {Long-Term Spectral Statistics for Voice Presentation + Attack Detection}, + Journal = {IEEE/ACM Trans. Audio, Speech and Lang. Proc.}, + Volume = {25}, + Number = {11}, + Pages = {2098--2111}, + year = 2017 + } + + **Parameters:** + + framerate: int + The sampling frequency of the signal (i.e the framerate ...) + + nfft: int + Number of points to compute the FFT + + debug: boolean + Plot stuff + """ + def __init__(self, window_size=25, framerate=25, nfft=64, concat=False, debug=False, **kwargs): + + super(LTSS, self).__init__() + + self.framerate = framerate + self.nfft = nfft + self.debug = debug + self.window_size = window_size + self.concat = concat + + def _get_ltss(self, signal): + + # log-magnitude of DFT coefficients + log_mags = [] + window_stride = int(self.window_size / 2) + + # go through windows + for w in range(0, (signal.shape[0] - self.window_size), window_stride): + fft = rfft(signal[w:w+self.window_size], n=self.nfft) + mags = numpy.zeros(int(self.nfft/2), dtype=numpy.float64) + mags[0] = abs(fft[0]) + index = 1 + for i in range(1, (fft.shape[0]-1), 2): + mags[index] = numpy.sqrt(fft[i]**2 + fft[i+1]**2) + if mags[index] < 1: + mags[index] = 1 + index += 1 + log_mags.append(numpy.log(mags)) + + # get the long term statistics + log_mags = numpy.array(log_mags) + mean = numpy.mean(log_mags, axis=0) + std = numpy.std(log_mags, axis=0) + ltss = numpy.concatenate([mean, std]) + return ltss + + + def __call__(self, signal): + """ + Computes the long-term spectral statistics for a given signal. + + **Parameters** + + signal: numpy.array + The signal + + **Returns:** + + feature: numpy.array + the long-term spectral statistics feature vector + """ + # sanity check + if signal.ndim == 1: + if numpy.isnan(numpy.sum(signal)): + return + if signal.ndim == 2 and (signal.shape[1] == 3): + for i in range(signal.shape[1]): + if numpy.isnan(numpy.sum(signal[:, i])): + return + + if signal.ndim == 1: + feature = self._get_ltss(signal) + + if signal.ndim == 2 and (signal.shape[1] == 3): + + if not self.concat: + feature = self._get_ltss(signal[:, 1]) + else: + ltss = [] + for i in range(signal.shape[1]): + ltss.append(self._get_ltss(signal[:, i])) + feature = numpy.concatenate([ltss[0], ltss[1], ltss[2]]) + + if numpy.isnan(numpy.sum(feature)): + logger.warn("Feature not extracted") + return + if numpy.sum(feature) == 0: + logger.warn("Feature not extracted") + return + + return feature diff --git a/bob/pad/face/extractor/__init__.py b/bob/pad/face/extractor/__init__.py index 1cbc4e4a45b17442bf173657a0e85c7224d76fd9..61cf772072c393134d3b98abad998d860b181848 100644 --- a/bob/pad/face/extractor/__init__.py +++ b/bob/pad/face/extractor/__init__.py @@ -9,6 +9,8 @@ from .FrequencySpectrum import FrequencySpectrum from .FreqFeatures import FreqFeatures from .NormalizeLength import NormalizeLength from .FFTFeatures import FFTFeatures +from .LTSS import LTSS +from .Autocorrelation import Autocorrelation def __appropriate__(*args): """Says object was actually declared here, and not in the import module.