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

[preprocessor] started PPGSecure implementation

parent d028b3f6
No related branches found
No related tags found
1 merge request!53WIP: rPPG as features for PAD
This commit is part of merge request !53. Comments created here will be created in the context of that merge request.
import numpy
import logging
logger = logging.getLogger("bob.pad.face")
from bob.bio.base.preprocessor import Preprocessor
from bob.rppg.base.utils import build_bandpass_filter
import bob.ip.dlib
from bob.rppg.cvpr14.extract_utils import get_mask
from bob.rppg.cvpr14.extract_utils import compute_average_colors_mask
class PPGSecure(Preprocessor):
"""
This class extract the pulse signal from a video sequence.
The pulse is extracted according to what is described in
the following article:
@InProceedings{nowara-afgr-2017,
Author = {E. M. Nowara and A. Sabharwal and A. Veeraraghavan},
Title = {P{PGS}ecure: {B}iometric {P}resentation {A}ttack
{D}etection {U}sing {P}hotopletysmograms},
BookTitle = {I{EEE} {I}ntl {C}onf on {A}utomatic {F}ace and
{G}esture {R}ecognition ({AFGR})},
Volume = {},
Number = {},
Pages = {56-62},
issn = {},
seq-number = {69},
year = 2017
}
**Parameters:**
indent: int
Indent (in pixels) to apply to keypoints to get the masks.
framerate: int
The framerate of the video sequence.
bp_order: int
The order of the bandpass filter
debug: boolean
Plot some stuff
"""
def __init__(self, indent = 10, framerate = 25, bp_order = 32, debug=False, **kwargs):
super(PPGSecure, self).__init__(**kwargs)
self.indent = indent
self.framerate = framerate
self.bp_order = bp_order
self.debug = debug
def __call__(self, frames, annotations):
"""
Compute the pulse signal for the given frame sequence
**Parameters:**
frames: :pyclass: `bob.bio.video.utils.FrameContainer`
Video data stored in the FrameContainer, see ``bob.bio.video.utils.FrameContainer``
for further details.
annotations: :py:class:`dict`
A dictionary containing annotations of the face bounding box.
Dictionary must be as follows ``{'topleft': (row, col), 'bottomright': (row, col)}``
**Returns:**
pulses: numpy.array of size (5, nb_frame)
The pulse signals from different area of the image
"""
video = frames.as_array()
nb_frames = video.shape[0]
# the mean of the green color of the different ROIs along the sequence
green_mean = numpy.zeros((nb_frames, 5), dtype='float64')
# build the bandpass filter one and for all
bandpass_filter = build_bandpass_filter(self.framerate, self.bp_order, min_freq=0.5, max_freq=5, plot=False)
# landmarks detection
detector = bob.ip.dlib.DlibLandmarkExtraction()
counter = 0
previous_ldms = None
for i, frame in enumerate(video):
logger.debug("Processing frame {}/{}".format(counter, nb_frames))
if self.debug:
from matplotlib import pyplot
pyplot.imshow(numpy.rollaxis(numpy.rollaxis(frame, 2),2))
pyplot.show()
# detect landmarks
try:
ldms = detector(frame)
except TypeError:
# looks like one video from replay mobile is upside down !
rotated_shape = bob.ip.base.rotated_output_shape(frame, 180)
frame_rotated = numpy.ndarray(rotated_shape, dtype=numpy.float64)
from bob.ip.base import rotate
bob.ip.base.rotate(frame, frame_rotated, 180)
frame_rotated = frame_rotated.astype(numpy.uint8)
logger.warning("Rotating again ...")
try:
ldms = detector(frame_rotated)
except TypeError:
ldms = previous_ldms
# so do nothing ...
logger.warning("No mask detected in frame {}".format(i))
face_color[i] = 0
continue
frame = frame_rotated
if self.debug:
from matplotlib import pyplot
display = numpy.copy(frame)
for p in ldms:
bob.ip.draw.plus(display, p, radius=5, color=(255, 0, 0))
pyplot.imshow(numpy.rollaxis(numpy.rollaxis(display, 2),2))
pyplot.show()
ldms = numpy.array(ldms)
masks = self._get_masks(ldms)
import sys
sys.exit()
#for i in range(5):
# face_color[i] = compute_average_colors_mask(frame, mask, self.debug)
#previous_ldms = ldms
#counter += 1
#pulse = numpy.zeros((nb_frames, 3), dtype='float64')
#for i in range(3):
# # detrend
# detrended = detrend(face_color[:, i], self.lambda_)
# # average
# averaged = average(detrended, self.window)
# # bandpass
# from scipy.signal import filtfilt
# pulse[:, i] = filtfilt(bandpass_filter, numpy.array([1]), averaged)
#if self.debug:
# from matplotlib import pyplot
# for i in range(3):
# f, ax = pyplot.subplots(2, sharex=True)
# ax[0].plot(range(face_color.shape[0]), face_color[:, i], 'g')
# ax[0].set_title('Original color signal')
# ax[1].plot(range(face_color.shape[0]), pulse[:, i], 'g')
# ax[1].set_title('Pulse signal')
# pyplot.show()
#return pulse
def _get_masks(ldms):
""" get the 5 masks for rPPG signal extraction
**Parameters**
ldms: numpy.array
The landmarks, as retrieved by bob.ip.dlib.DlibLandmarkExtraction()
**Returns**
masks: boolean
"""
# mask 1: forehead
# defined by 12 points: upper eyebrows (points 18 to 27)
# plus two additional points:
# - above 18, at a distance of (18-27)/2
# - above 27, at a distance of (18-27)/2
print(ldms)
print(ldms.shape)
mask_points = []
for i in range(17, 28):
mask_points.append([int(keypoints[k, 0]), int(keypoints[k, 1])]))
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment