#!/usr/bin/env python2
# -*- coding: utf-8 -*-
from bob.bio.video import FrameSelector, FrameContainer
from bob.pad.face.database import VideoPadFile # Used in MsuMfsdPadFile class
from bob.pad.base.database import PadDatabase
from bob.extension import rc
import os
import numpy as np
[docs]class MsuMfsdPadFile(VideoPadFile):
"""
A high level implementation of the File class for the MSU MFSD database.
"""
def __init__(self, f):
"""
**Parameters:**
``f`` : :py:class:`object`
An instance of the File class defined in the low level db interface
of the MSU MFSD database, in the bob.db.msu_mfsd_mod.models.py file.
"""
self.f = f
# this f is actually an instance of the File class that is defined in
# bob.db.msu_mfsd_mod.models and the PadFile class here needs
# client_id, path, attack_type, file_id for initialization. We have to
# convert information here and provide them to PadFile. attack_type is a
# little tricky to get here. Based on the documentation of PadFile:
# In cased of a spoofed data, this parameter should indicate what kind of spoofed attack it is.
# The default None value is interpreted that the PadFile is a genuine or real sample.
if f.is_real():
attack_type = None
else:
attack_type = "attack"
# attack_type is a string and I decided to make it like this for this
# particular database. You can do whatever you want for your own database.
super(MsuMfsdPadFile, self).__init__(
client_id=f.client_id, path=f.path, attack_type=attack_type, file_id=f.id
)
[docs] def load(
self,
directory=None,
extension=None,
frame_selector=FrameSelector(selection_style="all"),
):
"""
Overridden version of the load method defined in the ``VideoPadFile``.
**Parameters:**
``directory`` : :py:class:`str`
String containing the path to the MSU MFSD database.
Default: None
``extension`` : :py:class:`str`
Extension of the video files in the MSU MFSD database.
Note: ``extension`` value is not used in the code of this method.
Default: None
``frame_selector`` : ``FrameSelector``
The frame selector to use.
**Returns:**
``video_data`` : FrameContainer
Video data stored in the FrameContainer, see ``bob.bio.video.utils.FrameContainer``
for further details.
"""
_, extension = os.path.splitext(self.f.videofile()) # get file extension
video_data_array = self.f.load(directory=directory, extension=extension)
return frame_selector(video_data_array)
class MsuMfsdPadDatabase(PadDatabase):
"""
A high level implementation of the Database class for the MSU MFSD database.
"""
def __init__(
self,
protocol="grandtest", # grandtest is the default protocol for this database
original_directory=None,
original_extension=None,
annotation_directory=None,
annotation_extension='.json',
annotation_type='json',
**kwargs
):
"""
**Parameters:**
``protocol`` : :py:class:`str` or ``None``
The name of the protocol that defines the default experimental setup for this database.
``original_directory`` : :py:class:`str`
The directory where the original data of the database are stored.
``original_extension`` : :py:class:`str`
The file name extension of the original data.
``kwargs``
The arguments of the :py:class:`bob.bio.base.database.BioDatabase` base class constructor.
"""
from bob.db.msu_mfsd_mod import Database as LowLevelDatabase
self.db = LowLevelDatabase()
# Since the high level API expects different group names than what the low
# level API offers, you need to convert them when necessary
self.low_level_group_names = (
"train",
"devel",
"test",
) # group names in the low-level database interface
self.high_level_group_names = (
"train",
"dev",
"eval",
) # names are expected to be like that in objects() function
# Always use super to call parent class methods.
super(MsuMfsdPadDatabase, self).__init__(
name="msu-mfsd",
protocol=protocol,
original_directory=original_directory,
original_extension=original_extension,
**kwargs
)
@property
def original_directory(self):
return self.db.original_directory
@original_directory.setter
def original_directory(self, value):
self.db.original_directory = value
[docs] def objects(
self, groups=None, protocol=None, purposes=None, model_ids=None, **kwargs
):
"""
This function returns lists of MsuMfsdPadFile objects, which fulfill the given restrictions.
Keyword parameters:
``groups`` : :py:class:`str`
OR a list of strings.
The groups of which the clients should be returned.
Usually, groups are one or more elements of ('train', 'dev', 'eval')
``protocol`` : :py:class:`str`
The protocol for which the clients should be retrieved.
Note: this argument is not used in the code, because ``objects`` method of the
low-level BD interface of the MSU MFSD doesn't have ``protocol`` argument.
``purposes`` : :py:class:`str`
OR a list of strings.
The purposes for which File objects should be retrieved.
Usually it is either 'real' or 'attack'.
``model_ids``
This parameter is not supported in PAD databases yet.
**Returns:**
``files`` : [MsuMfsdPadFile]
A list of MsuMfsdPadFile objects.
"""
# Convert group names to low-level group names here.
groups = self.convert_names_to_lowlevel(
groups, self.low_level_group_names, self.high_level_group_names
)
# Since this database was designed for PAD experiments, nothing special
# needs to be done here.
files = self.db.objects(group=groups, cls=purposes, **kwargs)
files = [MsuMfsdPadFile(f) for f in files]
for f in files:
f.original_directory = self.original_directory
f.annotation_directory = self.annotation_directory
f.annotation_extension = self.annotation_extension
f.annotation_type = self.annotation_type
return files
[docs] def annotations(self, f):
"""
Return annotations for a given file object ``f``, which is an instance
of ``MsuMfsdPadFile`` defined in the HLDI of the MSU MFSD DB.
The ``load()`` method of ``MsuMfsdPadFile`` class (see above)
returns a video, therefore this method returns bounding-box annotations
for each video frame. The annotations are returned as dictionary of dictionaries.
**Parameters:**
``f`` : :py:class:`object`
An instance of ``MsuMfsdPadFile`` defined above.
**Returns:**
``annotations`` : :py:class:`dict`
A dictionary containing the annotations for each frame in the video.
Dictionary structure: ``annotations = {'1': frame1_dict, '2': frame1_dict, ...}``.
Where ``frameN_dict = {'topleft': (row, col), 'bottomright': (row, col)}``
is the dictionary defining the coordinates of the face bounding box in frame N.
"""
annots = f.f.bbx(
directory=self.original_directory
) # numpy array containing the face bounding box data for each video frame, returned data format described in the f.bbx() method of the low level interface
annotations = {} # dictionary to return
for frame_annots in annots:
topleft = (np.int(frame_annots[2]), np.int(frame_annots[1]))
bottomright = (
np.int(frame_annots[2] + frame_annots[4]),
np.int(frame_annots[1] + frame_annots[3]),
)
annotations[str(np.int(frame_annots[0]))] = {
"topleft": topleft,
"bottomright": bottomright,
}
return annotations