Skip to content
Snippets Groups Projects
Commit 594e57d4 authored by Guillaume HEUSCH's avatar Guillaume HEUSCH
Browse files

[extractor] added extractor that normalize the length of the features, and one...

[extractor] added extractor that normalize the length of the features, and one suing FFT to compute freq spectrum
parent a59bf0c7
Branches
Tags
1 merge request!53WIP: rPPG as features for PAD
Pipeline #
#!/usr/bin/env python
# encoding: utf-8
import numpy
from bob.bio.base.extractor import Extractor
import logging
logger = logging.getLogger("bob.pad.face")
class FFTFeatures(Extractor, object):
"""
Compute the Frequency Spectrum of the given signal.
The computation is made using numpy's rfft routine
**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, framerate=25, nfft=256, debug=False, **kwargs):
super(FFTFeatures, self).__init__(**kwargs)
self.framerate = framerate
self.nfft = nfft
self.debug = debug
def __call__(self, signal):
"""
Compute the frequency spectrum for the given signal.
**Parameters:**
signal: numpy.array
The signal
**Returns:**
freq: numpy.array
the frequency spectrum
"""
# sanity check
if signal.ndim == 1:
if numpy.isnan(numpy.sum(signal)):
return
if signal.ndim == 2 and (signal.shape[1] == 3):
if numpy.isnan(numpy.sum(signal[:, 1])):
return
output_dim = int((self.nfft / 2) + 1)
# get the frequencies
f = numpy.fft.fftfreq(self.nfft) * self.framerate
# we have a single pulse signal
if signal.ndim == 1:
fft = abs(numpy.fft.rfft(signal, n=self.nfft))
# we have 3 pulse signal (Li's preprocessing)
# in this case, return the signal corresponding to the green channel
if signal.ndim == 2 and (signal.shape[1] == 3):
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.debug:
from matplotlib import pyplot
pyplot.plot(f, fft, 'k')
pyplot.title('Power spectrum of the signal')
pyplot.show()
return fft
#!/usr/bin/env python
# encoding: utf-8
import numpy
from bob.bio.base.extractor import Extractor
import logging
logger = logging.getLogger("bob.pad.face")
class NormalizeLength(Extractor, object):
"""
Normalize the length of feature vectors, such that
they all have the same dimensions
**Parameters:**
length: int
The final length of the final feature vector
requires_training: boolean
This extractor actually may requires "training".
The goal here is to retrieve the length of the shortest sequence
debug: boolean
Plot stuff
"""
def __init__(self, length=-1, debug=False, requires_training=True, **kwargs):
super(NormalizeLength, self).__init__(requires_training=requires_training, **kwargs)
self.length = length
self.debug = debug
def __call__(self, signal):
"""
Normalize the length of the signal
**Parameters:**
signal: numpy.array
The signal
**Returns:**
signal: numpy.array
the signal with the provided length
"""
# we have a single pulse signal
if signal.ndim == 1:
signal = signal[:self.length]
# we have 3 pulse signal (Li's preprocessing)
# in this case, return the signal corresponding to the green channel
if signal.ndim == 2 and (signal.shape[1] == 3):
signal = signal[:self.length, 1]
if numpy.isnan(numpy.sum(signal)):
return
if signal.shape[0] < self.length:
logger.debug("signal shorter than training shape: {} vs {}".format(signal.shape[0], self.length))
import sys
sys.exit()
tmp = numpy.zeros((self.length), dtype=signal.dtype)
tmp[:, signal.shape[0]]
signal = tmp
if self.debug:
from matplotlib import pyplot
pyplot.plot(signal, 'k')
pyplot.title('Signal truncated')
pyplot.show()
return signal
def train(self, training_data, extractor_file):
"""
This function determines the shortest length across the training set.
It will be used to normalize the length of all the sequences.
**Parameters:**
training_data : [object] or [[object]]
A list of *preprocessed* data that can be used for training the extractor.
Data will be provided in a single list, if ``split_training_features_by_client = False`` was specified in the constructor,
otherwise the data will be split into lists, each of which contains the data of a single (training-)client.
extractor_file : str
The file to write.
This file should be readable with the :py:meth:`load` function.
"""
self.length = 100000
for i in range(len(training_data)):
if training_data[i].shape[0] < self.length:
self.length = training_data[i].shape[0]
logger.info("Signals will be truncated to {} dimensions".format(self.length))
......@@ -7,6 +7,8 @@ from .FrameDiffFeatures import FrameDiffFeatures
from .FrequencySpectrum import FrequencySpectrum
from .FreqFeatures import FreqFeatures
from .NormalizeLength import NormalizeLength
from .FFTFeatures import FFTFeatures
def __appropriate__(*args):
"""Says object was actually declared here, and not in the import module.
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment