Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
bob
bob.pad.face
Commits
03d4ac4e
Commit
03d4ac4e
authored
Feb 11, 2020
by
Anjith GEORGE
Browse files
Merge branch 'databases' into 'master'
Improve high level database interfaces See merge request
!104
parents
52dd9734
b3fb6d49
Pipeline
#36881
passed with stages
in 20 minutes and 43 seconds
Changes
8
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
bob/pad/face/database/batl.py
View file @
03d4ac4e
...
...
@@ -17,6 +17,14 @@ import bob.io.base
import
pkg_resources
map_file
=
pkg_resources
.
resource_filename
(
'bob.pad.face'
,
'lists/batl/idiap_converter_v2.json'
)
with
open
(
map_file
,
'r'
)
as
fp
:
map_dict
=
json
.
load
(
fp
)
two_d_attacks
=
[
'prints'
,
'replay'
]
three_d_attacks
=
[
'fakehead'
,
'glasses'
,
'flexiblemask'
,
'papermask'
,
'rigidmask'
,
'makeup'
]
BATL_FRAME_SHAPE
=
(
3
,
1920
,
1080
)
# =============================================================================
class
BatlPadFile
(
PadFile
):
...
...
@@ -77,7 +85,16 @@ class BatlPadFile(PadFile):
self
.
f
=
f
if
f
.
is_attack
():
attack_type
=
'attack'
pai_desc
=
map_dict
[
"_"
+
"_"
.
join
(
os
.
path
.
split
(
f
.
path
)[
-
1
].
split
(
"_"
)[
-
2
:])].
split
(
"_"
)[
-
1
]
if
pai_desc
in
two_d_attacks
:
category
=
'2D'
elif
pai_desc
in
three_d_attacks
:
category
=
'3D'
else
:
category
=
'Unknown'
attack_type
=
'attack/'
+
category
+
'/'
+
pai_desc
else
:
attack_type
=
None
...
...
@@ -160,6 +177,99 @@ class BatlPadFile(PadFile):
return
data_all_streams
@
property
def
annotations
(
self
):
file_path
=
os
.
path
.
join
(
self
.
annotations_temp_dir
,
self
.
f
.
path
+
".json"
)
if
not
os
.
path
.
isfile
(
file_path
):
# no file with annotations
try
:
# original values of the arguments of f:
stream_type_original
=
self
.
stream_type
reference_stream_type_original
=
self
.
reference_stream_type
warp_to_reference_original
=
self
.
warp_to_reference
convert_to_rgb_original
=
self
.
convert_to_rgb
crop_original
=
self
.
crop
video_data_only_original
=
self
.
video_data_only
self
.
stream_type
=
"color"
self
.
reference_stream_type
=
"color"
self
.
warp_to_reference
=
False
self
.
convert_to_rgb
=
False
self
.
crop
=
None
self
.
video_data_only
=
True
video
=
self
.
load
(
directory
=
self
.
original_directory
,
extension
=
self
.
original_extension
)
finally
:
# set arguments of f to the original values:
self
.
stream_type
=
stream_type_original
self
.
reference_stream_type
=
reference_stream_type_original
self
.
warp_to_reference
=
warp_to_reference_original
self
.
convert_to_rgb
=
convert_to_rgb_original
self
.
crop
=
crop_original
self
.
video_data_only
=
video_data_only_original
annotations
=
{}
for
idx
,
image
in
enumerate
(
video
.
as_array
()):
frame_annotations
=
detect_face_landmarks_in_image
(
image
,
method
=
self
.
landmark_detect_method
)
if
frame_annotations
:
annotations
[
str
(
idx
)]
=
frame_annotations
if
self
.
annotations_temp_dir
:
# if directory is not an empty string
bob
.
io
.
base
.
create_directories_safe
(
directory
=
os
.
path
.
split
(
file_path
)[
0
],
dryrun
=
False
)
with
open
(
file_path
,
'w+'
)
as
json_file
:
json_file
.
write
(
json
.
dumps
(
annotations
))
else
:
# if file with annotations exists load them from file
with
open
(
file_path
,
'r'
)
as
json_file
:
annotations
=
json
.
load
(
json_file
)
if
not
annotations
:
# if dictionary is empty
return
None
# If specified append annotations for the roi in the facial region:
if
self
.
append_color_face_roi_annot
:
file_path
=
pkg_resources
.
resource_filename
(
'bob.pad.face'
,
os
.
path
.
join
(
'lists/batl/color_skin_non_skin_annotations/'
,
"annotations_train_dev_set"
+
".json"
))
with
open
(
file_path
,
'r'
)
as
json_file
:
# open the file containing all annotations:
roi_annotations
=
json
.
load
(
json_file
)
# load the annotations
if
not
self
.
f
.
path
in
roi_annotations
:
# no annotations for this file
return
None
else
:
# annotations are available
annotations
[
'face_roi'
]
=
roi_annotations
[
self
.
f
.
path
]
return
annotations
@
property
def
frames
(
self
):
frame_container
=
self
.
load
(
directory
=
self
.
original_directory
,
extension
=
self
.
original_extension
)
return
frame_container
.
as_array
()
@
property
def
number_of_frames
(
self
):
return
len
(
self
.
frames
)
@
property
def
frame_shape
(
self
):
return
BATL_FRAME_SHAPE
# =============================================================================
class
BatlPadDatabase
(
PadDatabase
):
...
...
@@ -173,7 +283,8 @@ class BatlPadDatabase(PadDatabase):
protocol
=
'grandtest'
,
original_directory
=
rc
[
'bob.db.batl.directory'
],
original_extension
=
'.h5'
,
annotations_temp_dir
=
rc
[
'bob.pad.face.database.batl.annotations_temp_dir'
],
# annotations_temp_dir=rc['bob.pad.face.database.batl.annotations_temp_dir'],
annotations_temp_dir
=
None
,
landmark_detect_method
=
"mtcnn"
,
exclude_attacks_list
=
[
'makeup'
],
exclude_pai_all_sets
=
True
,
...
...
@@ -196,7 +307,7 @@ class BatlPadDatabase(PadDatabase):
"grandtest-depth-5" - baseline protocol, depth data only,
use 5 first frames.
"grandtest-color" - baseline protocol,
depth
data only, use all frames.
"grandtest-color" - baseline protocol,
color
data only, use all frames.
"grandtest-infrared-50-join_train_dev" - baseline protocol,
infrared data only, use 50 frames, join train and dev sets forming
...
...
@@ -205,9 +316,9 @@ class BatlPadDatabase(PadDatabase):
"grandtest-infrared-50-LOO_<unseen_attack>", for example "grandtest-infrared-50-LOO_fakehead"
- Leave One Out (LOO) protocol with fakehead attacks present only in the `test` set. The original partitioning
in the `grandtest` protocol is queried first and subselects the file list such
that the specified `unknown_attack` is removed from both `train` and `dev` sets.
that the specified `unknown_attack` is removed from both `train` and `dev` sets.
The `test` set will consist of only the selected `unknown_attack` and `bonafide` files.
This protocol is used to evaluate the robustness against attacks unseen in training.
This protocol is used to evaluate the robustness against attacks unseen in training.
.
"grandtest-color*infrared-50" - baseline protocol,
...
...
@@ -314,9 +425,9 @@ class BatlPadDatabase(PadDatabase):
def
unseen_attack_list_maker
(
self
,
files
,
unknown_attack
,
train
=
True
):
"""
Selects and returns a list of files for Leave One Out (LOO) protocols.
This utilizes the original partitioning in the `grandtest` protocol and subselects
the file list such that the specified `unknown_attack` is removed from both `train` and `dev` sets.
Selects and returns a list of files for Leave One Out (LOO) protocols.
This utilizes the original partitioning in the `grandtest` protocol and subselects
the file list such that the specified `unknown_attack` is removed from both `train` and `dev` sets.
The `test` set will consist of only the selected `unknown_attack` and `bonafide` files.
**Parameters:**
...
...
@@ -344,14 +455,14 @@ class BatlPadDatabase(PadDatabase):
attack_category
=
self
.
map_dict
[
"_"
+
"_"
.
join
(
os
.
path
.
split
(
file
.
path
)[
-
1
].
split
(
"_"
)[
-
2
:])].
split
(
"_"
)[
-
1
]
if
train
:
if
attack_category
==
unknown_attack
:
if
attack_category
in
unknown_attack
:
pass
else
:
mod_files
.
append
(
file
)
# everything except the attack specified is there
mod_files
.
append
(
file
)
# everything except the attack specified is there
if
not
train
:
if
attack_category
==
unknown_attack
or
attack_category
==
'bonafide'
:
if
attack_category
in
unknown_attack
or
attack_category
==
'bonafide'
:
mod_files
.
append
(
file
)
# only the attack mentioned and bonafides in testing
else
:
pass
...
...
@@ -365,7 +476,7 @@ class BatlPadDatabase(PadDatabase):
An example of protocols it can parse:
"grandtest-color-5" - grandtest protocol, color data only, use 5 first frames.
"grandtest-depth-5" - grandtest protocol, depth data only, use 5 first frames.
"grandtest-color" - grandtest protocol,
depth
data only, use all frames.
"grandtest-color" - grandtest protocol,
color
data only, use all frames.
**Parameters:**
...
...
@@ -392,7 +503,7 @@ class BatlPadDatabase(PadDatabase):
forming a single training set.
"""
possible_extras
=
[
'join_train_dev'
,
'LOO_fakehead'
,
'LOO_flexiblemask'
,
'LOO_glasses'
,
'LOO_papermask'
,
'LOO_prints'
,
'LOO_replay'
,
'LOO_rigidmask'
,
'LOO_makeup'
]
possible_extras
=
[
'join_train_dev'
,
'LOO_fakehead'
,
'LOO_flexiblemask'
,
'LOO_glasses'
,
'LOO_papermask'
,
'LOO_prints'
,
'LOO_replay'
,
'LOO_rigidmask'
,
'LOO_makeup'
,
'PrintReplay'
]
components
=
protocol
.
split
(
"-"
)
...
...
@@ -537,7 +648,6 @@ class BatlPadDatabase(PadDatabase):
``files`` : [BatlPadFile]
A list of BATLPadFile objects.
"""
if
protocol
is
None
:
protocol
=
self
.
protocol
...
...
@@ -555,7 +665,6 @@ class BatlPadDatabase(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
)
if
not
isinstance
(
groups
,
list
)
and
groups
is
not
None
and
not
isinstance
(
groups
,
str
):
# if a single group is given make it a list
groups
=
list
(
groups
)
...
...
@@ -597,7 +706,7 @@ class BatlPadDatabase(PadDatabase):
batl_files
=
batl_files
+
tbatl_files
if
'validation'
in
groups
:
if
'validation'
in
groups
:
tbatl_files
=
self
.
db
.
objects
(
protocol
=
protocol
,
groups
=
[
'validation'
],
purposes
=
purposes
,
**
kwargs
)
...
...
@@ -621,6 +730,49 @@ class BatlPadDatabase(PadDatabase):
# for the PrintReplay protocol
elif
extra
==
"PrintReplay"
:
batl_files
=
[]
# remove all attacks except replay and print
unknown_attack
=
[
"fakehead"
,
"flexiblemask"
,
"glasses"
,
"papermask"
,
"rigidmask"
,
"makeup"
]
if
'train'
in
groups
:
tbatl_files
=
self
.
db
.
objects
(
protocol
=
protocol
,
groups
=
[
'train'
],
purposes
=
purposes
,
**
kwargs
)
tbatl_files
=
self
.
unseen_attack_list_maker
(
tbatl_files
,
unknown_attack
,
train
=
True
)
batl_files
=
batl_files
+
tbatl_files
if
'validation'
in
groups
:
tbatl_files
=
self
.
db
.
objects
(
protocol
=
protocol
,
groups
=
[
'validation'
],
purposes
=
purposes
,
**
kwargs
)
tbatl_files
=
self
.
unseen_attack_list_maker
(
tbatl_files
,
unknown_attack
,
train
=
True
)
batl_files
=
batl_files
+
tbatl_files
if
'test'
in
groups
:
tbatl_files
=
self
.
db
.
objects
(
protocol
=
protocol
,
groups
=
[
'test'
],
purposes
=
purposes
,
**
kwargs
)
# train=True since we want to remove all attacks except replay and print
tbatl_files
=
self
.
unseen_attack_list_maker
(
tbatl_files
,
unknown_attack
,
train
=
True
)
batl_files
=
batl_files
+
tbatl_files
files
=
batl_files
else
:
# files = self._fix_funny_eyes_in_objects(protocol=protocol,
# groups=groups,
...
...
@@ -644,10 +796,15 @@ class BatlPadDatabase(PadDatabase):
files
=
[
f
for
f
in
files
if
os
.
path
.
split
(
f
.
path
)[
-
1
].
split
(
"_"
)[
-
2
:
-
1
][
0
]
!=
"5"
]
files
=
[
BatlPadFile
(
f
,
stream_type
,
max_frames
)
for
f
in
files
]
for
f
in
files
:
f
.
original_directory
=
self
.
original_directory
f
.
original_extension
=
self
.
original_extension
f
.
annotations_temp_dir
=
self
.
annotations_temp_dir
f
.
append_color_face_roi_annot
=
self
.
append_color_face_roi_annot
f
.
landmark_detect_method
=
self
.
landmark_detect_method
return
files
def
annotations
(
self
,
f
):
"""
Computes annotations for a given file object ``f``, which
...
...
@@ -674,82 +831,4 @@ class BatlPadDatabase(PadDatabase):
``frameN_dict`` contains coordinates of the
face bounding box and landmarks in frame N.
"""
file_path
=
os
.
path
.
join
(
self
.
annotations_temp_dir
,
f
.
f
.
path
+
".json"
)
if
not
os
.
path
.
isfile
(
file_path
):
# no file with annotations
# original values of the arguments of f:
stream_type_original
=
f
.
stream_type
reference_stream_type_original
=
f
.
reference_stream_type
warp_to_reference_original
=
f
.
warp_to_reference
convert_to_rgb_original
=
f
.
convert_to_rgb
crop_original
=
f
.
crop
video_data_only_original
=
f
.
video_data_only
f
.
stream_type
=
"color"
f
.
reference_stream_type
=
"color"
f
.
warp_to_reference
=
False
f
.
convert_to_rgb
=
False
f
.
crop
=
None
f
.
video_data_only
=
True
video
=
f
.
load
(
directory
=
self
.
original_directory
,
extension
=
self
.
original_extension
)
# set arguments of f to the original values:
f
.
stream_type
=
stream_type_original
f
.
reference_stream_type
=
reference_stream_type_original
f
.
warp_to_reference
=
warp_to_reference_original
f
.
convert_to_rgb
=
convert_to_rgb_original
f
.
crop
=
crop_original
f
.
video_data_only
=
video_data_only_original
annotations
=
{}
for
idx
,
image
in
enumerate
(
video
.
as_array
()):
frame_annotations
=
detect_face_landmarks_in_image
(
image
,
method
=
self
.
landmark_detect_method
)
if
frame_annotations
:
annotations
[
str
(
idx
)]
=
frame_annotations
if
self
.
annotations_temp_dir
:
# if directory is not an empty string
bob
.
io
.
base
.
create_directories_safe
(
directory
=
os
.
path
.
split
(
file_path
)[
0
],
dryrun
=
False
)
with
open
(
file_path
,
'w+'
)
as
json_file
:
json_file
.
write
(
json
.
dumps
(
annotations
))
else
:
# if file with annotations exists load them from file
with
open
(
file_path
,
'r'
)
as
json_file
:
annotations
=
json
.
load
(
json_file
)
if
not
annotations
:
# if dictionary is empty
return
None
# If specified append annotations for the roi in the facial region:
if
self
.
append_color_face_roi_annot
:
file_path
=
pkg_resources
.
resource_filename
(
'bob.pad.face'
,
os
.
path
.
join
(
'lists/batl/color_skin_non_skin_annotations/'
,
"annotations_train_dev_set"
+
".json"
)
)
with
open
(
file_path
,
'r'
)
as
json_file
:
# open the file containing all annotations:
roi_annotations
=
json
.
load
(
json_file
)
# load the annotations
if
not
f
.
f
.
path
in
roi_annotations
:
# no annotations for this file
return
None
else
:
# annotations are available
annotations
[
'face_roi'
]
=
roi_annotations
[
f
.
f
.
path
]
return
annotations
return
f
.
annotations
bob/pad/face/database/casiafasd.py
View file @
03d4ac4e
...
...
@@ -8,6 +8,8 @@ from bob.pad.base.database import PadDatabase
from
bob.pad.face.database
import
VideoPadFile
from
bob.db.base.utils
import
(
check_parameter_for_validity
,
check_parameters_for_validity
)
from
bob.db.base.annotations
import
read_annotation_file
from
bob.ip.facedetect
import
expected_eye_positions
,
BoundingBox
import
numpy
import
os
...
...
@@ -20,7 +22,7 @@ class CasiaFasdPadFile(VideoPadFile):
A high level implementation of the File class for the CASIA_FASD database.
"""
def
__init__
(
self
,
f
,
original_directory
=
None
):
def
__init__
(
self
,
f
,
original_directory
=
None
,
annotation_directory
=
None
):
"""
Parameters
----------
...
...
@@ -31,6 +33,7 @@ class CasiaFasdPadFile(VideoPadFile):
self
.
f
=
f
self
.
original_directory
=
original_directory
self
.
annotation_directory
=
annotation_directory
if
f
.
is_real
():
attack_type
=
None
...
...
@@ -93,6 +96,10 @@ class CasiaFasdPadFile(VideoPadFile):
def
annotations
(
self
):
"""Reads the annotations
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`
...
...
@@ -103,6 +110,10 @@ class CasiaFasdPadFile(VideoPadFile):
is the dictionary defining the coordinates of the face bounding box
in frame N.
"""
if
self
.
annotation_directory
is
not
None
:
path
=
self
.
make_path
(
self
.
annotation_directory
,
extension
=
".json"
)
return
read_annotation_file
(
path
,
annotation_type
=
"json"
)
annots
=
self
.
f
.
bbx
()
annotations
=
{}
for
i
,
v
in
enumerate
(
annots
):
...
...
@@ -110,6 +121,9 @@ class CasiaFasdPadFile(VideoPadFile):
bottomright
=
(
v
[
2
]
+
v
[
4
],
v
[
1
]
+
v
[
3
])
annotations
[
str
(
i
)]
=
{
'topleft'
:
topleft
,
'bottomright'
:
bottomright
}
size
=
(
bottomright
[
0
]
-
topleft
[
0
],
bottomright
[
1
]
-
topleft
[
1
])
bounding_box
=
BoundingBox
(
topleft
,
size
)
annotations
[
str
(
i
)].
update
(
expected_eye_positions
(
bounding_box
))
return
annotations
def
load
(
self
,
directory
=
None
,
extension
=
'.avi'
,
...
...
@@ -149,6 +163,7 @@ class CasiaFasdPadDatabase(PadDatabase):
# grandtest is the new modified protocol for this database
protocol
=
'grandtest'
,
original_directory
=
rc
[
'bob.db.casia_fasd.directory'
],
annotation_directory
=
None
,
**
kwargs
):
"""
Parameters
...
...
@@ -169,6 +184,7 @@ class CasiaFasdPadDatabase(PadDatabase):
protocol
=
protocol
,
original_directory
=
original_directory
,
original_extension
=
'.avi'
,
annotation_directory
=
annotation_directory
,
training_depends_on_protocol
=
True
,
**
kwargs
)
...
...
@@ -280,7 +296,8 @@ class CasiaFasdPadDatabase(PadDatabase):
db_mappings
[
t
+
'_'
+
q
])
files
.
append
(
CasiaFasdPadFile
(
File
(
filename
,
c
,
grp
),
self
.
original_directory
))
original_directory
=
self
.
original_directory
,
annotation_directory
=
self
.
annotation_directory
))
return
files
def
annotations
(
self
,
padfile
):
...
...
bob/pad/face/database/database.py
View file @
03d4ac4e
from
bob.pad.base.database
import
PadFile
import
bob.bio.video
import
bob.io.video
from
bob.db.base.annotations
import
read_annotation_file
class
VideoPadFile
(
PadFile
):
...
...
@@ -9,14 +11,15 @@ class VideoPadFile(PadFile):
def
__init__
(
self
,
attack_type
,
client_id
,
path
,
file_id
=
None
):
super
(
VideoPadFile
,
self
).
__init__
(
attack_type
=
attack_type
,
client_id
=
client_id
,
path
=
path
,
file_id
=
file_id
,
attack_type
=
attack_type
,
client_id
=
client_id
,
path
=
path
,
file_id
=
file_id
)
def
load
(
self
,
directory
=
None
,
extension
=
'.avi'
,
frame_selector
=
bob
.
bio
.
video
.
FrameSelector
(
selection_style
=
'all'
)):
def
load
(
self
,
directory
=
None
,
extension
=
".avi"
,
frame_selector
=
bob
.
bio
.
video
.
FrameSelector
(
selection_style
=
"all"
),
):
"""Loads the video file and returns in a :any:`bob.bio.video.FrameContainer`.
Parameters
...
...
@@ -34,3 +37,97 @@ class VideoPadFile(PadFile):
The loaded frames inside a frame container.
"""
return
frame_selector
(
self
.
make_path
(
directory
,
extension
))
def
check_original_directory_and_extension
(
self
):
if
not
hasattr
(
self
,
"original_directory"
):
raise
RuntimeError
(
"Please set the original_directory attribute of files in your "
"database's object method."
)
if
not
hasattr
(
self
,
"original_extension"
):
raise
RuntimeError
(
"Please set the original_extension attribute of files in your "
"database's object method."
)
@
property
def
frames
(
self
):
"""Returns an iterator of frames in the video.
If your database video files need to be loaded in a special way, you need to
override this property.
Returns
-------
collection.Iterator
An iterator returning frames of the video.
Raises
------
RuntimeError
In your database implementation, the original_directory and
original_extension attributes of the files need to be set when database's
object method is called.
"""
self
.
check_original_directory_and_extension
()
path
=
self
.
make_path
(
directory
=
self
.
original_directory
,
extension
=
self
.
original_extension
)
return
iter
(
bob
.
io
.
video
.
reader
(
path
))
@
property
def
number_of_frames
(
self
):
self
.
check_original_directory_and_extension
()
path
=
self
.
make_path
(
directory
=
self
.
original_directory
,
extension
=
self
.
original_extension
)
return
bob
.
io
.
video
.
reader
(
path
).
number_of_frames
@
property
def
frame_shape
(
self
):
"""Returns the size of each frame in this database.
This implementation assumes all videos and frames have the same shape.
It's best to override this method in your database implementation and return
a constant.
Returns
-------
(int, int, int)
The (Channels, Height, Width) sizes.
"""
self
.
check_original_directory_and_extension
()
path
=
self
.
make_path
(
directory
=
self
.
original_directory
,
extension
=
self
.
original_extension
)
frame
=
next
(
bob
.
io
.
video
.
reader
(
path
))
return
frame
.
shape
@
property
def
annotations
(
self
):
"""Reads the annotations
For this property to work, you need to set ``annotation_directory``,
``annotation_extension``, and ``annotation_type`` attributes of the files when
database's object method is called.
Returns
-------
dict
The annotations as a dictionary.
"""
if
not
(
hasattr
(
self
,
"annotation_directory"
)
and
hasattr
(
self
,
"annotation_extension"
)
and
hasattr
(
self
,
"annotation_type"
)
):
raise
RuntimeError
(
"For this property to work, you need to set ``annotation_directory``, "
"``annotation_extension``, and ``annotation_type`` attributes of the "
"files when database's object method is called."
)
if
self
.
annotation_directory
is
None
:
return
None
annotation_file
=
self
.
make_path
(
directory
=
self
.
annotation_directory
,
extension
=
self
.
annotation_extension
)
return
read_annotation_file
(
annotation_file
,
self
.
annotation_type
)
bob/pad/face/database/maskattack.py
View file @
03d4ac4e
...
...
@@ -5,7 +5,7 @@ import os
import
numpy
as
np
import
bob.io.video
from
bob.bio.video
import
FrameSelector
,
FrameContainer
from
bob.pad.face.database
import
VideoPadFile
from
bob.pad.face.database
import
VideoPadFile
from
bob.pad.base.database
import
PadDatabase
class
MaskAttackPadFile
(
VideoPadFile
):
...
...
@@ -17,18 +17,18 @@ class MaskAttackPadFile(VideoPadFile):
f : :py:class:`object`
An instance of the File class defined in the low level db interface
of the 3DMAD database, in the bob.db.maskattack.models.py file.
"""
def
__init__
(
self
,
f
):
"""Init function
Parameters
----------
f : :py:class:`object`
An instance of the File class defined in the low level db interface
of the 3DMAD database, in the bob.db.maskattack.models.py file.