Skip to content
Snippets Groups Projects
Commit f98a0386 authored by Yannick DAYER's avatar Yannick DAYER
Browse files

Renaming replay-mobile files

parent 211cf0bd
No related branches found
No related tags found
1 merge request!106Vulnerability framework - CSV datasets
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
Replay-mobile CSV database interface instantiation Replay-mobile CSV database interface instantiation
""" """
from bob.bio.face.database.replaymobile_csv import ReplayMobileDatabase from bob.bio.face.database.replaymobile import ReplayMobileDatabase
import bob.core import bob.core
......
#!/usr/bin/env python
from bob.bio.face.database import ReplayMobileBioDatabase
from bob.bio.base.pipelines.vanilla_biometrics import DatabaseConnector
from bob.extension import rc
replay_mobile_directory = rc["bob.db.replay_mobile.directory"]
database = DatabaseConnector(
ReplayMobileBioDatabase(
original_directory=replay_mobile_directory,
original_extension=".mov",
protocol="grandtest-licit",
),
annotation_type="bounding-box",
fixed_positions=None,
)
#!/usr/bin/env python
from bob.bio.face.database import ReplayMobileBioDatabase
from bob.bio.base.pipelines.vanilla_biometrics import DatabaseConnector
from bob.extension import rc
replay_mobile_directory = rc["bob.db.replay_mobile.directory"]
database = DatabaseConnector(
ReplayMobileBioDatabase(
original_directory=replay_mobile_directory,
original_extension=".mov",
protocol="grandtest-spoof",
),
annotation_type="bounding-box",
fixed_positions=None,
# Only compare with spoofs from the same target identity
allow_scoring_with_all_biometric_references=False,
)
#!/usr/bin/env python #!/usr/bin/env python
# vim: set fileencoding=utf-8 : # Yannick Dayer <yannick.dayer@idiap.ch>
""" The Replay-Mobile Database for face spoofing implementation of from bob.bio.base.database import CSVDataset, CSVToSampleLoaderBiometrics
bob.bio.base.database.BioDatabase interface.""" from bob.pipelines.datasets.sample_loaders import AnnotationsLoader
from bob.pipelines.sample import DelayedSample
from .database import FaceBioFile from bob.extension.download import get_file
from bob.bio.base.database import BioDatabase from bob.io.video import reader
from bob.extension import rc from bob.extension import rc
import bob.core
from sklearn.base import TransformerMixin, BaseEstimator
from sklearn.pipeline import make_pipeline
import functools
import os.path
import numpy
class ReplayMobileBioFile(FaceBioFile): logger = bob.core.log.setup("bob.bio.face")
"""FaceBioFile implementation of the Replay Mobile Database"""
def __init__(self, f): def load_frame_from_file_replaymobile(file_name, frame, capturing_device):
super(ReplayMobileBioFile, self).__init__(client_id=f.client_id, path=f.path, file_id=f.id) """Loads a single frame from a video file for replay-mobile.
self._f = f
def load(self, directory=None, extension=None): This function uses bob's video reader utility that does not load the full
if extension in (None, '.mov'): video in memory to just access one frame.
return self._f.load(directory, extension)
else: Parameters
return super(ReplayMobileBioFile, self).load(directory, extension) ----------
file_name: str
The video file to load the frames from
frame: None or list of int
The index of the frame to load.
@property capturing device: str
def annotations(self): 'mobile' devices' frames will be flipped vertically.
return self._f.annotations Other devices' frames will not be flipped.
Returns
-------
class ReplayMobileBioDatabase(BioDatabase): images: 3D numpy array
The frame of the video in bob format (channel, height, width)
""" """
ReplayMobile database implementation of :py:class:`bob.bio.base.database.BioDatabase` interface. logger.debug(f"Extracting frame {frame} from '{file_name}'")
It is an extension of an SQL-based database interface, which directly talks to ReplayMobile database, for video_reader = reader(file_name)
verification experiments (good to use in bob.bio.base framework). image = video_reader[frame]
# Image captured by the 'mobile' device are flipped vertically.
# (Images were captured horizontally and bob.io.video does not read the
# metadata correctly, whether it was on the right or left side)
if capturing_device == "mobile":
image = numpy.flip(image, 2)
# Convert to bob format (channel, height, width)
image = numpy.transpose(image, (0, 2, 1))
return image
def read_frame_annotation_file_replaymobile(file_name, frame):
"""Returns the bounding-box for one frame of a video file of replay-mobile.
Given an annnotation file location and a frame number, returns the bounding
box coordinates corresponding to the frame.
The replay-mobile annotation files are composed of 4 columns and N rows for
N frames of the video:
120 230 40 40
125 230 40 40
...
<x> <y> <w> <h>
Parameters
----------
file_name: str
The complete annotation file path and name (with extension).
frame: int
The video frame index.
""" """
logger.debug(f"Reading annotation file '{file_name}', frame {frame}.")
if not file_name:
return None
def __init__(self, max_number_of_frames=None, if not os.path.exists(file_name):
annotation_directory=None, raise IOError(f"The annotation file '{file_name}' was not found")
annotation_extension='.json',
annotation_type='json', with open(file_name, 'r') as f:
original_directory=rc['bob.db.replaymobile.directory'], # One line is one frame, each line contains a bounding box coordinates
original_extension='.mov', line = f.readlines()[frame]
name='replay-mobile',
**kwargs): positions = line.split(' ')
from bob.db.replaymobile.verificationprotocol import Database as LowLevelDatabase
self._db = LowLevelDatabase( if len(positions) != 4:
max_number_of_frames, raise ValueError(f"The content of '{file_name}' was not correct for frame {frame} ({positions})")
original_directory=original_directory,
original_extension=original_extension, annotations = {
annotation_directory=annotation_directory, 'topleft': (float(positions[1]), float(positions[0])),
annotation_extension=annotation_extension, 'bottomright':(
annotation_type=annotation_type, float(positions[1])+float(positions[3]),
float(positions[0])+float(positions[2])
) )
}
# call base class constructors to open a session to the database return annotations
super(ReplayMobileBioDatabase, self).__init__(
name=name,
original_directory=original_directory,
original_extension=original_extension,
annotation_directory=annotation_directory,
annotation_extension=annotation_extension,
annotation_type=annotation_type,
**kwargs)
self._kwargs['max_number_of_frames'] = max_number_of_frames
@property class ReplayMobileCSVFrameSampleLoader(CSVToSampleLoaderBiometrics):
def original_directory(self): """A loader transformer returning a specific frame of a video file.
return self._db.original_directory
@original_directory.setter This is specifically tailored for replay-mobile. It uses a specific loader
def original_directory(self, value): that takes the capturing device as input.
self._db.original_directory = value """
def __init__(
self,
dataset_original_directory="",
extension="",
reference_id_equal_subject_id=True,
):
super().__init__(
data_loader=None,
extension=extension,
dataset_original_directory=dataset_original_directory,
)
self.reference_id_equal_subject_id = reference_id_equal_subject_id
@property def convert_row_to_sample(self, row, header):
def original_extension(self): """Creates a set of samples given a row of the CSV protocol definition.
return self._db.original_extension """
path = row[0]
reference_id = row[1]
id = row[2] # Will be used as 'key'
@original_extension.setter kwargs = dict([[str(h).lower(), r] for h, r in zip(header[3:], row[3:])])
def original_extension(self, value): if self.reference_id_equal_subject_id:
self._db.original_extension = value kwargs["subject_id"] = reference_id
else:
if "subject_id" not in kwargs:
raise ValueError(f"`subject_id` not available in {header}")
# One row leads to multiple samples (different frames)
all_samples = [DelayedSample(
functools.partial(
load_frame_from_file_replaymobile,
file_name=os.path.join(self.dataset_original_directory, path + self.extension),
frame=frame,
capturing_device=kwargs["capturing_device"],
),
key=f"{id}_{frame}",
path=path,
reference_id=reference_id,
frame=frame,
**kwargs,
) for frame in range(12,251,24)]
return all_samples
@property
def annotation_directory(self):
return self._db.annotation_directory
@annotation_directory.setter class FrameBoundingBoxAnnotationLoader(AnnotationsLoader):
def annotation_directory(self, value): """A transformer that adds bounding-box to a sample from annotations files.
self._db.annotation_directory = value
@property Parameters
def annotation_extension(self): ----------
return self._db.annotation_extension
@annotation_extension.setter annotation_directory: str or None
def annotation_extension(self, value): """
self._db.annotation_extension = value def __init__(self,
annotation_directory=None,
annotation_extension=".face",
**kwargs
):
super().__init__(
annotation_directory=annotation_directory,
annotation_extension=annotation_extension,
**kwargs
)
@property def transform(self, X):
def annotation_type(self): """Adds the bounding-box annotations to a series of samples.
return self._db.annotation_type """
if self.annotation_directory is None:
return None
@annotation_type.setter annotated_samples = []
def annotation_type(self, value): for x in X:
self._db.annotation_type = value
def protocol_names(self): # Build the path to the annotation files structure
return self._db.protocols() annotation_file = os.path.join(
self.annotation_directory, x.path + self.annotation_extension
)
def groups(self): annotated_samples.append(
return self._db.groups() DelayedSample(
x._load,
parent=x,
delayed_attributes=dict(
annotations=functools.partial(
read_frame_annotation_file_replaymobile,
file_name=annotation_file,
frame=int(x.frame),
)
),
)
)
def annotations(self, myfile): return annotated_samples
"""Will return the bounding box annotation of nth frame of the video."""
return myfile.annotations
def model_ids_with_protocol(self, groups=None, protocol=None, **kwargs): class ReplayMobileDatabase(CSVDataset):
return self._db.model_ids_with_protocol(groups, protocol, **kwargs) """Database interface that loads a csv definition for replay-mobile
def objects(self, groups=None, protocol=None, purposes=None, model_ids=None, **kwargs): Looks for the protocol definition files (structure of CSV files). If not
return [ReplayMobileBioFile(f) for f in self._db.objects(groups, protocol, purposes, model_ids, **kwargs)] present, downloads them.
Then sets the data and annotation paths from __init__ parameters or from
the configuration (``bob config`` command).
def arrange_by_client(self, files): Parameters
client_files = {} ----------
for file in files:
if str(file.client_id) not in client_files:
client_files[str(file.client_id)] = []
client_files[str(file.client_id)].append(file)
files_by_clients = [] protocol_name: str
for client in sorted(client_files.keys()): The protocol to use
files_by_clients.append(client_files[client])
return files_by_clients protocol_definition_path: str or None
Specifies a path to download the database definition to.
If None: Downloads and uses the ``bob_data`` config.
(See :py:fct:`bob.extension.download.get_file`)
data_path: str or None
Overrides the config-defined data location.
If None: uses the ``bob.db.replaymobile.directory`` config.
If None and the config does not exist, set as cwd.
annotation_path: str or None
Overrides the config-defined annotation files location.
If None: uses the ``bob.db.replaymobile.annotation_directory`` config.
If None and the config does not exist, set as
``{data_path}/faceloc/rect``.
"""
def __init__(
self,
protocol_name="bio-grandtest",
protocol_definition_path=None,
data_path=None,
annotation_path=None,
**kwargs
):
if protocol_definition_path is None:
# Downloading database description files if it is not specified
urls = [
"https://www.idiap.ch/software/bob/databases/latest/replay-mobile-csv.tar.gz",
"http://www.idiap.ch/software/bob/databases/latest/replay-mobile-csv.tar.gz",
]
protocol_definition_path = get_file("replay-mobile-csv.tar.gz", urls)
if data_path is None:
# Defaults to cwd if config not defined
data_path = rc.get("bob.db.replaymobile.directory", "")
if annotation_path is None:
# Defaults to {data_path}/faceloc/rect if config not defined
annotation_path = rc.get(
"bob.db.replaymobile.annotation_directory",
os.path.join(data_path, "faceloc/rect/")
)
logger.info(f"Database: Loading database definition from '{protocol_definition_path}'.")
logger.info(f"Database: Defining data files path as '{data_path}'.")
logger.info(f"Database: Defining annotation files path as '{annotation_path}'.")
super().__init__(
protocol_definition_path,
protocol_name,
csv_to_sample_loader=make_pipeline(
ReplayMobileCSVFrameSampleLoader(
dataset_original_directory=data_path,
extension=".mov",
),
FrameBoundingBoxAnnotationLoader(
annotation_directory=annotation_path,
annotation_extension=".face",
),
),
**kwargs
)
self.annotation_type = "bounding-box"
self.fixed_positions = None
#!/usr/bin/env python
# Yannick Dayer <yannick.dayer@idiap.ch>
from bob.bio.base.database import CSVDataset, CSVToSampleLoaderBiometrics
from bob.pipelines.datasets.sample_loaders import AnnotationsLoader
from bob.pipelines.sample import DelayedSample
from bob.extension.download import get_file
from bob.io.video import reader
from bob.extension import rc
import bob.core
from sklearn.base import TransformerMixin, BaseEstimator
from sklearn.pipeline import make_pipeline
import functools
import os.path
import numpy
logger = bob.core.log.setup("bob.bio.face")
def load_frame_from_file_replaymobile(file_name, frame, capturing_device):
"""Loads a single frame from a video file for replay-mobile.
This function uses bob's video reader utility that does not load the full
video in memory to just access one frame.
Parameters
----------
file_name: str
The video file to load the frames from
frame: None or list of int
The index of the frame to load.
capturing device: str
'mobile' devices' frames will be flipped vertically.
Other devices' frames will not be flipped.
Returns
-------
images: 3D numpy array
The frame of the video in bob format (channel, height, width)
"""
logger.debug(f"Extracting frame {frame} from '{file_name}'")
video_reader = reader(file_name)
image = video_reader[frame]
# Image captured by the 'mobile' device are flipped vertically.
# (Images were captured horizontally and bob.io.video does not read the
# metadata correctly, whether it was on the right or left side)
if capturing_device == "mobile":
image = numpy.flip(image, 2)
# Convert to bob format (channel, height, width)
image = numpy.transpose(image, (0, 2, 1))
return image
def read_frame_annotation_file_replaymobile(file_name, frame):
"""Returns the bounding-box for one frame of a video file of replay-mobile.
Given an annnotation file location and a frame number, returns the bounding
box coordinates corresponding to the frame.
The replay-mobile annotation files are composed of 4 columns and N rows for
N frames of the video:
120 230 40 40
125 230 40 40
...
<x> <y> <w> <h>
Parameters
----------
file_name: str
The complete annotation file path and name (with extension).
frame: int
The video frame index.
"""
logger.debug(f"Reading annotation file '{file_name}', frame {frame}.")
if not file_name:
return None
if not os.path.exists(file_name):
raise IOError(f"The annotation file '{file_name}' was not found")
with open(file_name, 'r') as f:
# One line is one frame, each line contains a bounding box coordinates
line = f.readlines()[frame]
positions = line.split(' ')
if len(positions) != 4:
raise ValueError(f"The content of '{file_name}' was not correct for frame {frame} ({positions})")
annotations = {
'topleft': (float(positions[1]), float(positions[0])),
'bottomright':(
float(positions[1])+float(positions[3]),
float(positions[0])+float(positions[2])
)
}
return annotations
class ReplayMobileCSVFrameSampleLoader(CSVToSampleLoaderBiometrics):
"""A loader transformer returning a specific frame of a video file.
This is specifically tailored for replay-mobile. It uses a specific loader
that takes the capturing device as input.
"""
def __init__(
self,
dataset_original_directory="",
extension="",
reference_id_equal_subject_id=True,
):
super().__init__(
data_loader=None,
extension=extension,
dataset_original_directory=dataset_original_directory,
)
self.reference_id_equal_subject_id = reference_id_equal_subject_id
def convert_row_to_sample(self, row, header):
"""Creates a set of samples given a row of the CSV protocol definition.
"""
path = row[0]
reference_id = row[1]
id = row[2] # Will be used as 'key'
kwargs = dict([[str(h).lower(), r] for h, r in zip(header[3:], row[3:])])
if self.reference_id_equal_subject_id:
kwargs["subject_id"] = reference_id
else:
if "subject_id" not in kwargs:
raise ValueError(f"`subject_id` not available in {header}")
# One row leads to multiple samples (different frames)
all_samples = [DelayedSample(
functools.partial(
load_frame_from_file_replaymobile,
file_name=os.path.join(self.dataset_original_directory, path + self.extension),
frame=frame,
capturing_device=kwargs["capturing_device"],
),
key=f"{id}_{frame}",
path=path,
reference_id=reference_id,
frame=frame,
**kwargs,
) for frame in range(12,251,24)]
return all_samples
class FrameBoundingBoxAnnotationLoader(AnnotationsLoader):
"""A transformer that adds bounding-box to a sample from annotations files.
Parameters
----------
annotation_directory: str or None
"""
def __init__(self,
annotation_directory=None,
annotation_extension=".face",
**kwargs
):
super().__init__(
annotation_directory=annotation_directory,
annotation_extension=annotation_extension,
**kwargs
)
def transform(self, X):
"""Adds the bounding-box annotations to a series of samples.
"""
if self.annotation_directory is None:
return None
annotated_samples = []
for x in X:
# Build the path to the annotation files structure
annotation_file = os.path.join(
self.annotation_directory, x.path + self.annotation_extension
)
annotated_samples.append(
DelayedSample(
x._load,
parent=x,
delayed_attributes=dict(
annotations=functools.partial(
read_frame_annotation_file_replaymobile,
file_name=annotation_file,
frame=int(x.frame),
)
),
)
)
return annotated_samples
class ReplayMobileDatabase(CSVDataset):
"""Database interface that loads a csv definition for replay-mobile
Looks for the protocol definition files (structure of CSV files). If not
present, downloads them.
Then sets the data and annotation paths from __init__ parameters or from
the configuration (``bob config`` command).
Parameters
----------
protocol_name: str
The protocol to use
protocol_definition_path: str or None
Specifies a path to download the database definition to.
If None: Downloads and uses the ``bob_data`` config.
(See :py:fct:`bob.extension.download.get_file`)
data_path: str or None
Overrides the config-defined data location.
If None: uses the ``bob.db.replaymobile.directory`` config.
If None and the config does not exist, set as cwd.
annotation_path: str or None
Overrides the config-defined annotation files location.
If None: uses the ``bob.db.replaymobile.annotation_directory`` config.
If None and the config does not exist, set as
``{data_path}/faceloc/rect``.
"""
def __init__(
self,
protocol_name="bio-grandtest",
protocol_definition_path=None,
data_path=None,
annotation_path=None,
**kwargs
):
if protocol_definition_path is None:
# Downloading database description files if it is not specified
urls = [
"https://www.idiap.ch/software/bob/databases/latest/replay-mobile-csv.tar.gz",
"http://www.idiap.ch/software/bob/databases/latest/replay-mobile-csv.tar.gz",
]
protocol_definition_path = get_file("replay-mobile-csv.tar.gz", urls)
if data_path is None:
# Defaults to cwd if config not defined
data_path = rc.get("bob.db.replaymobile.directory", "")
if annotation_path is None:
# Defaults to {data_path}/faceloc/rect if config not defined
annotation_path = rc.get(
"bob.db.replaymobile.annotation_directory",
os.path.join(data_path, "faceloc/rect/")
)
logger.info(f"Database: Loading database definition from '{protocol_definition_path}'.")
logger.info(f"Database: Defining data files path as '{data_path}'.")
logger.info(f"Database: Defining annotation files path as '{annotation_path}'.")
super().__init__(
protocol_definition_path,
protocol_name,
csv_to_sample_loader=make_pipeline(
ReplayMobileCSVFrameSampleLoader(
dataset_original_directory=data_path,
extension=".mov",
),
FrameBoundingBoxAnnotationLoader(
annotation_directory=annotation_path,
annotation_extension=".face",
),
),
**kwargs
)
self.annotation_type = "bounding-box"
self.fixed_positions = None
...@@ -107,9 +107,7 @@ setup( ...@@ -107,9 +107,7 @@ setup(
"multipie-pose = bob.bio.face.config.database.multipie_pose:database", "multipie-pose = bob.bio.face.config.database.multipie_pose:database",
"replay-img-licit = bob.bio.face.config.database.replay_licit:database", "replay-img-licit = bob.bio.face.config.database.replay_licit:database",
"replay-img-spoof = bob.bio.face.config.database.replay_spoof:database", "replay-img-spoof = bob.bio.face.config.database.replay_spoof:database",
"replaymobile-img-licit = bob.bio.face.config.database.replaymobile_licit:database", "replaymobile-img = bob.bio.face.config.database.replaymobile:database",
"replaymobile-img-spoof = bob.bio.face.config.database.replaymobile_spoof:database",
"replaymobile-img-csv = bob.bio.face.config.database.replaymobile_csv:database",
"fargo = bob.bio.face.config.database.fargo:database", "fargo = bob.bio.face.config.database.fargo:database",
"meds = bob.bio.face.config.database.meds:database", "meds = bob.bio.face.config.database.meds:database",
"morph = bob.bio.face.config.database.morph:database", "morph = bob.bio.face.config.database.morph:database",
...@@ -176,8 +174,7 @@ setup( ...@@ -176,8 +174,7 @@ setup(
"multipie-pose = bob.bio.face.config.database.multipie_pose", "multipie-pose = bob.bio.face.config.database.multipie_pose",
"replay-img-licit = bob.bio.face.config.database.replay_licit", "replay-img-licit = bob.bio.face.config.database.replay_licit",
"replay-img-spoof = bob.bio.face.config.database.replay_spoof", "replay-img-spoof = bob.bio.face.config.database.replay_spoof",
"replaymobile-img-licit = bob.bio.face.config.database.replaymobile_licit", "replaymobile-img = bob.bio.face.config.database.replaymobile",
"replaymobile-img-spoof = bob.bio.face.config.database.replaymobile_spoof",
"fargo = bob.bio.face.config.database.fargo", "fargo = bob.bio.face.config.database.fargo",
"meds = bob.bio.face.config.database.meds", "meds = bob.bio.face.config.database.meds",
"morph = bob.bio.face.config.database.morph", "morph = bob.bio.face.config.database.morph",
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment