Skip to content
Snippets Groups Projects
Commit f42b7f3e authored by Olegs NIKISINS's avatar Olegs NIKISINS
Browse files

Merge branch 'frames' into 'master'

Implement frame by frame loading in replay and replay-mobile

See merge request !33
parents de546856 43fddd1b
Branches
Tags
1 merge request!33Implement frame by frame loading in replay and replay-mobile
Pipeline #
#!/usr/bin/env python2 #!/usr/bin/env python2
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
#==============================================================================
# Used in ReplayMobilePadFile class # Used in ReplayMobilePadFile class
from bob.bio.video import FrameSelector, FrameContainer from bob.bio.video import FrameSelector, FrameContainer
from bob.pad.face.database import VideoPadFile # Used in ReplayPadFile class
from bob.pad.base.database import PadDatabase from bob.pad.base.database import PadDatabase
from bob.pad.face.database import VideoPadFile # Used in ReplayPadFile class
#============================================================================== from bob.pad.face.utils import frames, number_of_frames
class ReplayPadFile(VideoPadFile): class ReplayPadFile(VideoPadFile):
""" """
A high level implementation of the File class for the REPLAY-ATTACK database. A high level implementation of the File class for the REPLAY-ATTACK
database.
""" """
def __init__(self, f): def __init__(self, f):
""" """
**Parameters:** Parameters
----------
``f`` : :py:class:`object` f : object
An instance of the File class defined in the low level db interface An instance of the File class defined in the low level db interface
of the Replay database, in the bob.db.replay.models.py file. of the Replay database, in the bob.db.replay.models.py file.
""" """
self.f = f self.f = f
# this f is actually an instance of the File class that is defined in # this f is actually an instance of the File class that is defined in
# bob.db.replay.models and the PadFile class here needs # bob.db.replay.models and the PadFile class here needs client_id,
# client_id, path, attack_type, file_id for initialization. We have to # path, attack_type, file_id for initialization. We have to convert
# convert information here and provide them to PadFile. attack_type is a # information here and provide them to PadFile. attack_type is a little
# little tricky to get here. Based on the documentation of PadFile: # tricky to get here. Based on the documentation of PadFile: In cased
# In cased of a spoofed data, this parameter should indicate what kind of spoofed attack it is. # of a spoofed data, this parameter should indicate what kind of
# The default None value is interpreted that the PadFile is a genuine or real sample. # spoofed attack it is. The default None value is interpreted that the
# PadFile is a genuine or real sample.
if f.is_real(): if f.is_real():
attack_type = None attack_type = None
else: else:
attack_type = 'attack' attack_type = 'attack'
# attack_type is a string and I decided to make it like this for this # 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. # particular database. You can do whatever you want for your own
# database.
super(ReplayPadFile, self).__init__( super(ReplayPadFile, self).__init__(
client_id=f.client_id, client_id=f.client_id,
...@@ -48,45 +47,45 @@ class ReplayPadFile(VideoPadFile): ...@@ -48,45 +47,45 @@ class ReplayPadFile(VideoPadFile):
attack_type=attack_type, attack_type=attack_type,
file_id=f.id) file_id=f.id)
def make_path(self, directory=None, extension='.mov'):
# path to the video file
return self.f.make_path(directory=directory, extension=extension)
#==============================================================================
class ReplayPadDatabase(PadDatabase): class ReplayPadDatabase(PadDatabase):
""" """
A high level implementation of the Database class for the REPLAY-ATTACK database. A high level implementation of the Database class for the REPLAY-ATTACK
database.
""" """
def __init__( def __init__(
self, self,
protocol='grandtest', # grandtest is the default protocol for this database # grandtest is the default protocol for this database
protocol='grandtest',
original_directory=None, original_directory=None,
original_extension=None, original_extension=None,
**kwargs): **kwargs):
""" """
**Parameters:** Parameters
----------
``protocol`` : :py:class:`str` or ``None`` protocol : str or None
The name of the protocol that defines the default experimental setup for this database. The name of the protocol that defines the default experimental
setup for this database.
``original_directory`` : :py:class:`str` original_directory : str
The directory where the original data of the database are stored. The directory where the original data of the database are stored.
``original_extension`` : :py:class:`str` original_extension : str
The file name extension of the original data. The file name extension of the original data.
``kwargs`` kwargs
The arguments of the :py:class:`bob.bio.base.database.BioDatabase` base class constructor. The arguments of the :py:class:`bob.bio.base.database.BioDatabase`
base class constructor.
""" """
from bob.db.replay import Database as LowLevelDatabase from bob.db.replay import Database as LowLevelDatabase
self.db = LowLevelDatabase() self.db = LowLevelDatabase()
# Since the high level API expects different group names than what the low # Since the high level API expects different group names than what the
# level API offers, you need to convert them when necessary # low level API offers, you need to convert them when necessary
self.low_level_group_names = ( self.low_level_group_names = (
'train', 'devel', 'train', 'devel',
'test') # group names in the low-level database interface 'test') # group names in the low-level database interface
...@@ -110,7 +109,6 @@ class ReplayPadDatabase(PadDatabase): ...@@ -110,7 +109,6 @@ class ReplayPadDatabase(PadDatabase):
def original_directory(self, value): def original_directory(self, value):
self.db.original_directory = value self.db.original_directory = value
#==========================================================================
def objects(self, def objects(self,
groups=None, groups=None,
protocol=None, protocol=None,
...@@ -118,31 +116,32 @@ class ReplayPadDatabase(PadDatabase): ...@@ -118,31 +116,32 @@ class ReplayPadDatabase(PadDatabase):
model_ids=None, model_ids=None,
**kwargs): **kwargs):
""" """
This function returns lists of ReplayPadFile objects, which fulfill the given restrictions. This function returns lists of ReplayPadFile objects, which fulfill the
given restrictions.
Keyword parameters:
``groups`` : :py:class:`str` Parameters
OR a list of strings. ----------
groups : :obj:`str` or [:obj:`str`]
The groups of which the clients should be returned. The groups of which the clients should be returned.
Usually, groups are one or more elements of ('train', 'dev', 'eval') Usually, groups are one or more elements of
('train', 'dev', 'eval')
``protocol`` : :py:class:`str` protocol : str
The protocol for which the clients should be retrieved. The protocol for which the clients should be retrieved.
The protocol is dependent on your database. The protocol is dependent on your database.
If you do not have protocols defined, just ignore this field. If you do not have protocols defined, just ignore this field.
``purposes`` : :py:class:`str` purposes : :obj:`str` or [:obj:`str`]
OR a list of strings.
The purposes for which File objects should be retrieved. The purposes for which File objects should be retrieved.
Usually it is either 'real' or 'attack'. Usually it is either 'real' or 'attack'.
``model_ids`` model_ids
This parameter is not supported in PAD databases yet This parameter is not supported in PAD databases yet
**kwargs
**Returns:** Returns
-------
``files`` : [ReplayPadFile] files : [ReplayPadFile]
A list of ReplayPadFile objects. A list of ReplayPadFile objects.
""" """
...@@ -156,32 +155,35 @@ class ReplayPadDatabase(PadDatabase): ...@@ -156,32 +155,35 @@ class ReplayPadDatabase(PadDatabase):
files = [ReplayPadFile(f) for f in files] files = [ReplayPadFile(f) for f in files]
return files return files
#==========================================================================
def annotations(self, f): def annotations(self, f):
""" """
Return annotations for a given file object ``f``, which is an instance Return annotations for a given file object ``f``, which is an instance
of ``ReplayPadFile`` defined in the HLDI of the Replay-Attack DB. of ``ReplayPadFile`` defined in the HLDI of the Replay-Attack DB. The
The ``load()`` method of ``ReplayPadFile`` class (see above) ``load()`` method of ``ReplayPadFile`` class (see above) returns a
returns a video, therefore this method returns bounding-box annotations video, therefore this method returns bounding-box annotations for each
for each video frame. The annotations are returned as dictionary of dictionaries. video frame. The annotations are returned as a dictionary of
dictionaries.
**Parameters:**
Parameters
``f`` : :py:class:`object` ----------
An instance of ``ReplayPadFile`` defined above. f : :any:`ReplayPadFile`
An instance of :any:`ReplayPadFile`.
**Returns:**
Returns
``annotations`` : :py:class:`dict` -------
A dictionary containing the annotations for each frame in the video. annotations : :py:class:`dict`
Dictionary structure: ``annotations = {'1': frame1_dict, '2': frame1_dict, ...}``. A dictionary containing the annotations for each frame in the
Where ``frameN_dict = {'topleft': (row, col), 'bottomright': (row, col)}`` video. Dictionary structure:
is the dictionary defining the coordinates of the face bounding box in frame N. ``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( # numpy array containing the face bounding box data for each video
directory=self.original_directory # frame, returned data format described in the f.bbx() method of the
) # 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 # low level interface
annots = f.f.bbx(directory=self.original_directory)
annotations = {} # dictionary to return annotations = {} # dictionary to return
...@@ -197,3 +199,51 @@ class ReplayPadDatabase(PadDatabase): ...@@ -197,3 +199,51 @@ class ReplayPadDatabase(PadDatabase):
} }
return annotations return annotations
def frames(self, padfile):
"""Yields the frames of the padfile one by one.
Parameters
----------
padfile : :any:`ReplayPadFile`
The high-level replay pad file
Yields
------
:any:`numpy.array`
A frame of the video. The size is (3, 240, 320).
"""
vfilename = padfile.make_path(
directory=self.original_directory,
extension=self.original_extension)
for retval in frames(vfilename):
yield retval
def number_of_frames(self, padfile):
"""Returns the number of frames in a video file.
Parameters
----------
padfile : :any:`ReplayPadFile`
The high-level pad file
Returns
-------
int
The number of frames.
"""
vfilename = padfile.make_path(
directory=self.original_directory,
extension=self.original_extension)
return number_of_frames(vfilename)
@property
def frame_shape(self):
"""Returns the size of each frame in this database.
Returns
-------
(int, int, int)
The (#Channels, Height, Width) which is (3, 240, 320).
"""
return (3, 240, 320)
#!/usr/bin/env python2 #!/usr/bin/env python2
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
#==============================================================================
# Used in ReplayMobilePadFile class # Used in ReplayMobilePadFile class
from bob.bio.video import FrameSelector, FrameContainer from bob.bio.video import FrameSelector
from bob.pad.face.database import VideoPadFile # Used in ReplayMobilePadFile class
from bob.pad.base.database import PadDatabase from bob.pad.base.database import PadDatabase
from bob.pad.face.database import VideoPadFile
from bob.pad.face.utils import frames, number_of_frames
import numpy
# documentation imports
import bob.bio.video
#==============================================================================
class ReplayMobilePadFile(VideoPadFile): class ReplayMobilePadFile(VideoPadFile):
""" """
A high level implementation of the File class for the Replay-Mobile database. A high level implementation of the File class for the Replay-Mobile
database.
""" """
def __init__(self, f): def __init__(self, f):
""" """
**Parameters:** Parameters
----------
``f`` : :py:class:`object` f : object
An instance of the File class defined in the low level db interface An instance of the File class defined in the low level db interface
of the Replay-Mobile database, in the bob.db.replaymobile.models.py file. of the Replay-Mobile database, in the bob.db.replaymobile.models.py
file.
""" """
self.f = f self.f = f
# this f is actually an instance of the File class that is defined in # this f is actually an instance of the File class that is defined in
# bob.db.replaymobile.models and the PadFile class here needs # bob.db.replaymobile.models and the PadFile class here needs
# client_id, path, attack_type, file_id for initialization. We have to # client_id, path, attack_type, file_id for initialization. We have to
# convert information here and provide them to PadFile. attack_type is a # convert information here and provide them to PadFile. attack_type is
# little tricky to get here. Based on the documentation of PadFile: # 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. # In cased of a spoofed data, this parameter should indicate what kind
# The default None value is interpreted that the PadFile is a genuine or real sample. # of spoofed attack it is. The default None value is interpreted that
# the PadFile is a genuine or real sample.
if f.is_real(): if f.is_real():
attack_type = None attack_type = None
else: else:
attack_type = 'attack' attack_type = 'attack'
# attack_type is a string and I decided to make it like this for this # 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. # particular database. You can do whatever you want for your own
# database.
super(ReplayMobilePadFile, self).__init__( super(ReplayMobilePadFile, self).__init__(
client_id=f.client_id, client_id=f.client_id,
...@@ -46,27 +50,27 @@ class ReplayMobilePadFile(VideoPadFile): ...@@ -46,27 +50,27 @@ class ReplayMobilePadFile(VideoPadFile):
attack_type=attack_type, attack_type=attack_type,
file_id=f.id) file_id=f.id)
#========================================================================== def load(self, directory=None, extension='.mov',
def load(self, directory=None, extension='.mov', frame_selector=FrameSelector(selection_style='all')): frame_selector=FrameSelector(selection_style='all')):
""" """
Overridden version of the load method defined in the ``VideoPadFile``. Overridden version of the load method defined in the ``VideoPadFile``.
**Parameters:** Parameters
----------
``directory`` : :py:class:`str` directory : str
String containing the path to the Replay-Mobile database. String containing the path to the Replay-Mobile database.
``extension`` : :py:class:`str` extension : str
Extension of the video files in the Replay-Mobile database. Extension of the video files in the Replay-Mobile database.
``frame_selector`` : ``FrameSelector`` frame_selector : :any:`bob.bio.video.FrameSelector`
The frame selector to use. The frame selector to use.
**Returns:** Returns
-------
``video_data`` : FrameContainer video_data : :any:`bob.bio.video.FrameContainer`
Video data stored in the FrameContainer, see ``bob.bio.video.utils.FrameContainer`` Video data stored in the FrameContainer, see
for further details. ``bob.bio.video.utils.FrameContainer`` for further details.
""" """
video_data_array = self.f.load( video_data_array = self.f.load(
...@@ -75,40 +79,44 @@ class ReplayMobilePadFile(VideoPadFile): ...@@ -75,40 +79,44 @@ class ReplayMobilePadFile(VideoPadFile):
return frame_selector(video_data_array) return frame_selector(video_data_array)
#==============================================================================
class ReplayMobilePadDatabase(PadDatabase): class ReplayMobilePadDatabase(PadDatabase):
""" """
A high level implementation of the Database class for the Replay-Mobile database. A high level implementation of the Database class for the Replay-Mobile
database.
""" """
def __init__( def __init__(
self, self,
protocol='grandtest', # grandtest is the default protocol for this database # grandtest is the default protocol for this database
protocol='grandtest',
original_directory=None, original_directory=None,
original_extension=None, original_extension=None,
**kwargs): **kwargs):
""" """
**Parameters:** Parameters
----------
``protocol`` : :py:class:`str` or ``None`` protocol : str or None
The name of the protocol that defines the default experimental setup for this database. The name of the protocol that defines the default experimental
setup for this database.
``original_directory`` : :py:class:`str` original_directory : str
The directory where the original data of the database are stored. The directory where the original data of the database are stored.
``original_extension`` : :py:class:`str` original_extension : str
The file name extension of the original data. The file name extension of the original data.
``kwargs`` kwargs
The arguments of the :py:class:`bob.bio.base.database.BioDatabase` base class constructor. The arguments of the :py:class:`bob.bio.base.database.BioDatabase`
base class constructor.
""" """
from bob.db.replaymobile import Database as LowLevelDatabase from bob.db.replaymobile import Database as LowLevelDatabase
self.db = LowLevelDatabase() self.db = LowLevelDatabase()
# Since the high level API expects different group names than what the low # Since the high level API expects different group names than what the
# level API offers, you need to convert them when necessary # low level API offers, you need to convert them when necessary
self.low_level_group_names = ( self.low_level_group_names = (
'train', 'devel', 'train', 'devel',
'test') # group names in the low-level database interface 'test') # group names in the low-level database interface
...@@ -132,7 +140,6 @@ class ReplayMobilePadDatabase(PadDatabase): ...@@ -132,7 +140,6 @@ class ReplayMobilePadDatabase(PadDatabase):
def original_directory(self, value): def original_directory(self, value):
self.db.original_directory = value self.db.original_directory = value
#==========================================================================
def objects(self, def objects(self,
groups=None, groups=None,
protocol=None, protocol=None,
...@@ -140,31 +147,34 @@ class ReplayMobilePadDatabase(PadDatabase): ...@@ -140,31 +147,34 @@ class ReplayMobilePadDatabase(PadDatabase):
model_ids=None, model_ids=None,
**kwargs): **kwargs):
""" """
This function returns lists of ReplayMobilePadFile objects, which fulfill the given restrictions. This function returns lists of ReplayMobilePadFile objects, which
fulfill the given restrictions.
Keyword parameters: Parameters
----------
``groups`` : :py:class:`str` groups : str
OR a list of strings. OR a list of strings.
The groups of which the clients should be returned. The groups of which the clients should be returned.
Usually, groups are one or more elements of ('train', 'dev', 'eval') Usually, groups are one or more elements of
('train', 'dev', 'eval')
``protocol`` : :py:class:`str` protocol : str
The protocol for which the clients should be retrieved. The protocol for which the clients should be retrieved.
The protocol is dependent on your database. The protocol is dependent on your database.
If you do not have protocols defined, just ignore this field. If you do not have protocols defined, just ignore this field.
``purposes`` : :py:class:`str` purposes : str
OR a list of strings. OR a list of strings.
The purposes for which File objects should be retrieved. The purposes for which File objects should be retrieved.
Usually it is either 'real' or 'attack'. Usually it is either 'real' or 'attack'.
``model_ids`` model_ids
This parameter is not supported in PAD databases yet This parameter is not supported in PAD databases yet
**kwargs
**Returns:** Returns
-------
``files`` : [ReplayMobilePadFile] files : [ReplayMobilePadFile]
A list of ReplayMobilePadFile objects. A list of ReplayMobilePadFile objects.
""" """
...@@ -180,32 +190,35 @@ class ReplayMobilePadDatabase(PadDatabase): ...@@ -180,32 +190,35 @@ class ReplayMobilePadDatabase(PadDatabase):
return files return files
#==========================================================================
def annotations(self, f): def annotations(self, f):
""" """
Return annotations for a given file object ``f``, which is an instance Return annotations for a given file object ``f``, which is an instance
of ``ReplayMobilePadFile`` defined in the HLDI of the Replay-Mobile DB. of ``ReplayMobilePadFile`` defined in the HLDI of the Replay-Mobile DB.
The ``load()`` method of ``ReplayMobilePadFile`` class (see above) The ``load()`` method of ``ReplayMobilePadFile`` class (see above)
returns a video, therefore this method returns bounding-box annotations returns a video, therefore this method returns bounding-box annotations
for each video frame. The annotations are returned as dictionary of dictionaries. for each video frame. The annotations are returned as dictionary of
dictionaries.
**Parameters:**
Parameters
``f`` : :py:class:`object` ----------
An instance of ``ReplayMobilePadFile`` defined above. f : :any:`ReplayMobilePadFile`
An instance of :any:`ReplayMobilePadFile` defined above.
**Returns:**
Returns
``annotations`` : :py:class:`dict` -------
A dictionary containing the annotations for each frame in the video. annotations : :py:class:`dict`
Dictionary structure: ``annotations = {'1': frame1_dict, '2': frame1_dict, ...}``. A dictionary containing the annotations for each frame in the
Where ``frameN_dict = {'topleft': (row, col), 'bottomright': (row, col)}`` video. Dictionary structure:
is the dictionary defining the coordinates of the face bounding box in frame N. ``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( # numpy array containing the face bounding box data for each video
directory=self.original_directory # frame, returned data format described in the f.bbx() method of the
) # 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 # low level interface
annots = f.f.bbx(directory=self.original_directory)
annotations = {} # dictionary to return annotations = {} # dictionary to return
...@@ -221,3 +234,55 @@ class ReplayMobilePadDatabase(PadDatabase): ...@@ -221,3 +234,55 @@ class ReplayMobilePadDatabase(PadDatabase):
} }
return annotations return annotations
def frames(self, padfile):
"""Yields the frames of the padfile one by one.
Parameters
----------
padfile : :any:`ReplayMobilePadFile`
The high-level replay pad file
Yields
------
:any:`numpy.array`
A frame of the video. The size is (3, 1280, 720).
"""
vfilename = padfile.make_path(
directory=self.original_directory,
extension=self.original_extension)
is_not_tablet = not padfile._f.is_tablet()
for frame in frames(vfilename):
frame = numpy.rollaxis(frame, 2, 1)
if is_not_tablet:
frame = frame[:, ::-1, :]
yield frame
def number_of_frames(self, padfile):
"""Returns the number of frames in a video file.
Parameters
----------
padfile : :any:`ReplayPadFile`
The high-level pad file
Returns
-------
int
The number of frames.
"""
vfilename = padfile.make_path(
directory=self.original_directory,
extension=self.original_extension)
return number_of_frames(vfilename)
@property
def frame_shape(self):
"""Returns the size of each frame in this database.
Returns
-------
(int, int, int)
The (#Channels, Height, Width) which is (3, 1280, 720).
"""
return (3, 1280, 720)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment