Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
bob.pad.face
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package Registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
bob
bob.pad.face
Commits
7c72e080
Commit
7c72e080
authored
5 years ago
by
Amir MOHAMMADI
Browse files
Options
Downloads
Patches
Plain Diff
[replaymobile][replay][msu_mfsd] improve interfaces to make files independent
parent
006b0c9e
No related branches found
Branches containing commit
No related tags found
Tags containing commit
1 merge request
!104
Improve high level database interfaces
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
bob/pad/face/database/msu_mfsd.py
+49
-44
49 additions, 44 deletions
bob/pad/face/database/msu_mfsd.py
bob/pad/face/database/replay.py
+103
-58
103 additions, 58 deletions
bob/pad/face/database/replay.py
bob/pad/face/database/replay_mobile.py
+3
-36
3 additions, 36 deletions
bob/pad/face/database/replay_mobile.py
with
155 additions
and
138 deletions
bob/pad/face/database/msu_mfsd.py
+
49
−
44
View file @
7c72e080
#!/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
This diff is collapsed.
Click to expand it.
bob/pad/face/database/replay.py
+
103
−
58
View file @
7c72e080
#!/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
This diff is collapsed.
Click to expand it.
bob/pad/face/database/replay_mobile.py
+
3
−
36
View file @
7c72e080
...
...
@@ -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
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment