Commit 7c72e080 authored by Amir MOHAMMADI's avatar Amir MOHAMMADI

[replaymobile][replay][msu_mfsd] improve interfaces to make files independent

parent 006b0c9e
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
#==============================================================================
# Used in ReplayMobilePadFile class
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
#==============================================================================
class MsuMfsdPadFile(VideoPadFile):
"""
A high level implementation of the File class for the MSU MFSD database.
......@@ -40,18 +34,20 @@ class MsuMfsdPadFile(VideoPadFile):
if f.is_real():
attack_type = None
else:
attack_type = 'attack'
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)
#==========================================================================
def load(self, directory=None, extension=None, frame_selector=FrameSelector(selection_style='all')):
client_id=f.client_id, path=f.path, attack_type=attack_type, file_id=f.id
)
def load(
self,
directory=None,
extension=None,
frame_selector=FrameSelector(selection_style="all"),
):
"""
Overridden version of the load method defined in the ``VideoPadFile``.
......@@ -76,26 +72,27 @@ class MsuMfsdPadFile(VideoPadFile):
for further details.
"""
_, extension = os.path.splitext(
self.f.videofile()) # get file extension
_, extension = os.path.splitext(self.f.videofile()) # get file extension
video_data_array = self.f.load(
directory=directory, extension=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,
**kwargs):
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:**
......@@ -119,19 +116,24 @@ class MsuMfsdPadDatabase(PadDatabase):
# 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
"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
"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',
name="msu-mfsd",
protocol=protocol,
original_directory=original_directory,
original_extension=original_extension,
**kwargs)
**kwargs
)
@property
def original_directory(self):
......@@ -141,13 +143,9 @@ class MsuMfsdPadDatabase(PadDatabase):
def original_directory(self, value):
self.db.original_directory = value
#==========================================================================
def objects(self,
groups=None,
protocol=None,
purposes=None,
model_ids=None,
**kwargs):
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.
......@@ -179,16 +177,21 @@ class MsuMfsdPadDatabase(PadDatabase):
# 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)
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
#==========================================================================
def annotations(self, f):
"""
Return annotations for a given file object ``f``, which is an instance
......@@ -220,12 +223,14 @@ class MsuMfsdPadDatabase(PadDatabase):
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]))
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
"topleft": topleft,
"bottomright": bottomright,
}
return annotations
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Used in ReplayMobilePadFile class
from bob.pad.base.database import PadDatabase
from bob.pad.base.database import PadDatabase # Used in ReplayMobilePadFile class
from bob.pad.face.database import VideoPadFile # Used in ReplayPadFile class
from bob.pad.face.utils import frames, number_of_frames
from bob.extension import rc
from bob.ip.facedetect import expected_eye_positions, BoundingBox
from bob.db.base.annotations import read_annotation_file
REPLAY_ATTACK_FRAME_SHAPE = (3, 240, 320)
class ReplayPadFile(VideoPadFile):
......@@ -35,16 +37,73 @@ class ReplayPadFile(VideoPadFile):
if f.is_real():
attack_type = None
else:
attack_type = 'attack'
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(ReplayPadFile, self).__init__(
client_id=f.client_id,
path=f.path,
attack_type=attack_type,
file_id=f.id)
client_id=f.client_id, path=f.path, attack_type=attack_type, file_id=f.id
)
@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 REPLAY_ATTACK_FRAME_SHAPE
@property
def annotations(self):
"""
Return annotations as a dictionary of dictionaries.
If the file object has an attribute of annotation_directory, it will read
annotations from there instead of loading annotations that are shipped with the
database.
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.
"""
if (
hasattr(self, "annotation_directory")
and self.annotation_directory is not None
):
path = self.make_path(self.annotation_directory, extension=".json")
return read_annotation_file(path, annotation_type="json")
# 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
annots = self.f.bbx(directory=self.original_directory)
annotations = {} # dictionary to return
for fn, frame_annots in enumerate(annots):
topleft = (frame_annots[2], frame_annots[1])
bottomright = (
frame_annots[2] + frame_annots[4],
frame_annots[1] + frame_annots[3],
)
annotations[str(fn)] = {"topleft": topleft, "bottomright": bottomright}
size = (bottomright[0] - topleft[0], bottomright[1] - topleft[1])
bounding_box = BoundingBox(topleft, size)
annotations[str(fn)].update(expected_eye_positions(bounding_box))
return annotations
class ReplayPadDatabase(PadDatabase):
......@@ -54,12 +113,14 @@ class ReplayPadDatabase(PadDatabase):
"""
def __init__(
self,
# grandtest is the default protocol for this database
protocol='grandtest',
original_directory=rc['bob.db.replay.directory'],
original_extension='.mov',
**kwargs):
self,
# grandtest is the default protocol for this database
protocol="grandtest",
original_directory=rc["bob.db.replay.directory"],
original_extension=".mov",
annotation_directory=None,
**kwargs
):
"""
Parameters
----------
......@@ -86,19 +147,25 @@ class ReplayPadDatabase(PadDatabase):
# 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
"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
"train",
"dev",
"eval",
) # names are expected to be like that in objects() function
# Always use super to call parent class methods.
super(ReplayPadDatabase, self).__init__(
name='replay',
name="replay",
protocol=protocol,
original_directory=original_directory,
original_extension=original_extension,
**kwargs)
annotation_directory=annotation_directory,
**kwargs
)
@property
def original_directory(self):
......@@ -108,12 +175,9 @@ class ReplayPadDatabase(PadDatabase):
def original_directory(self, value):
self.db.original_directory = value
def objects(self,
groups=None,
protocol=None,
purposes=None,
model_ids=None,
**kwargs):
def objects(
self, groups=None, protocol=None, purposes=None, model_ids=None, **kwargs
):
"""
This function returns lists of ReplayPadFile objects, which fulfill the
given restrictions.
......@@ -146,12 +210,19 @@ class ReplayPadDatabase(PadDatabase):
# 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)
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(
protocol=protocol, groups=groups, cls=purposes, **kwargs)
protocol=protocol, groups=groups, cls=purposes, **kwargs
)
files = [ReplayPadFile(f) for f in files]
# set the attributes
for f in files:
f.original_directory = self.original_directory
f.original_extension = self.original_extension
f.annotation_directory = self.annotation_directory
return files
def annotations(self, f):
......@@ -178,26 +249,7 @@ class ReplayPadDatabase(PadDatabase):
is the dictionary defining the coordinates of the face bounding box
in frame N.
"""
# 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
annots = f.f.bbx(directory=self.original_directory)
annotations = {} # dictionary to return
for fn, frame_annots in enumerate(annots):
topleft = (frame_annots[2], frame_annots[1])
bottomright = (frame_annots[2] + frame_annots[4],
frame_annots[1] + frame_annots[3])
annotations[str(fn)] = {
'topleft': topleft,
'bottomright': bottomright
}
return annotations
return f.annotations
def frames(self, padfile):
"""Yields the frames of the padfile one by one.
......@@ -212,11 +264,7 @@ class ReplayPadDatabase(PadDatabase):
: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
return padfile.frames
def number_of_frames(self, padfile):
"""Returns the number of frames in a video file.
......@@ -231,10 +279,7 @@ class ReplayPadDatabase(PadDatabase):
int
The number of frames.
"""
vfilename = padfile.make_path(
directory=self.original_directory,
extension=self.original_extension)
return number_of_frames(vfilename)
return padfile.number_of_frames
@property
def frame_shape(self):
......@@ -245,4 +290,4 @@ class ReplayPadDatabase(PadDatabase):
(int, int, int)
The (#Channels, Height, Width) which is (3, 240, 320).
"""
return (3, 240, 320)
return REPLAY_ATTACK_FRAME_SHAPE
......@@ -5,48 +5,13 @@
from bob.bio.video import FrameSelector
from bob.pad.base.database import PadDatabase
from bob.pad.face.database import VideoPadFile
from bob.pad.face.utils import frames, number_of_frames
from bob.pad.face.utils import number_of_frames
from bob.db.base.annotations import read_annotation_file
import numpy
from bob.extension import rc
REPLAYMOBILE_FRAME_SHAPE = (3, 1280, 720)
def replaymobile_annotations(lowlevelfile, 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
annots = lowlevelfile.bbx(directory=original_directory)
annotations = {} # dictionary to return
for fn, frame_annots in enumerate(annots):
topleft = (frame_annots[1], frame_annots[0])
bottomright = (frame_annots[1] + frame_annots[3],
frame_annots[0] + frame_annots[2])
annotations[str(fn)] = {
'topleft': topleft,
'bottomright': bottomright
}
return annotations
def replaymobile_frames(lowlevelfile, original_directory):
vfilename = lowlevelfile.make_path(
directory=original_directory,
extension='.mov')
is_not_tablet = not lowlevelfile.is_tablet()
for frame in frames(vfilename):
frame = numpy.rollaxis(frame, 2, 1)
if is_not_tablet:
frame = frame[:, ::-1, :]
yield frame
class ReplayMobilePadFile(VideoPadFile):
"""
A high level implementation of the File class for the Replay-Mobile
......@@ -116,6 +81,7 @@ class ReplayMobilePadFile(VideoPadFile):
@property
def annotations(self):
from bob.db.replaymobile.models import replaymobile_annotations
if self.annotation_directory is not None:
# return the external annotations
annotations = read_annotation_file(
......@@ -129,6 +95,7 @@ class ReplayMobilePadFile(VideoPadFile):
@property
def frames(self):
from bob.db.replaymobile.models import replaymobile_frames
return replaymobile_frames(self.f, self.original_directory)
@property
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment