diff --git a/bob/paper/nir_patch_pooling/config/mlfp.py b/bob/paper/nir_patch_pooling/config/mlfp.py index 5e8a1ac0c46e367e8499512365512721aae92a37..79b1c84b388460ee19459e2eb98c43cb1f4060ce 100644 --- a/bob/paper/nir_patch_pooling/config/mlfp.py +++ b/bob/paper/nir_patch_pooling/config/mlfp.py @@ -1,4 +1,5 @@ #!/usr/bin/env python +# -*- coding: utf-8 -*- """ Database configuration for NIR data from MLFP database for detection of @@ -8,7 +9,6 @@ mask-based presentation attacks from bob.paper.nir_patch_pooling.database import MLFPDatabase from bob.extension import rc - database = MLFPDatabase( original_directory=rc["bob.db.mlfp.directory"], annotation_directory=rc["bob.db.mlfp.annotation_directory"], diff --git a/bob/paper/nir_patch_pooling/config/run.sh b/bob/paper/nir_patch_pooling/config/run.sh index 4e002d94195c12cc83efd2f7de8da33953ab59fb..38606a48d3e46ef63cfb247ccb3de12498e02f7d 100755 --- a/bob/paper/nir_patch_pooling/config/run.sh +++ b/bob/paper/nir_patch_pooling/config/run.sh @@ -30,7 +30,7 @@ cmd="spoof.py ${db_config} patch_pooling_lr.py -s ${output_directory} -vv --grid #echo ${cmd} $cmd -cmd="bob pad metriics -v -e ${output_directory}/${protocol}/scores/scores-{dev,eval}" +cmd="bob pad metrics -v -e ${output_directory}/${protocol}/scores/scores-{dev,eval}" echo ${cmd} #$cmd diff --git a/bob/paper/nir_patch_pooling/database/mlfp.py b/bob/paper/nir_patch_pooling/database/mlfp.py index 9046e2c64b7e5bbe1efda90cbcfd00461665cd4b..56cf92523458ac08e62590aa0e27d54e27bdee82 100644 --- a/bob/paper/nir_patch_pooling/database/mlfp.py +++ b/bob/paper/nir_patch_pooling/database/mlfp.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """ -Implementation of database interface of MLFP dataset for Face PAD. +Implementation of database interface for MLFP dataset. This protocol caters to only NIR subset of MLFP dataset. @author: Ketan Kotwal @@ -11,7 +11,7 @@ This protocol caters to only NIR subset of MLFP dataset. from bob.pad.base.database import FileListPadDatabase from bob.pad.face.database import VideoPadFile from bob.bio.video import FrameSelector, FrameContainer -import bob.io.base +from bob.io.base import HDF5File import json import numpy as np @@ -36,7 +36,12 @@ class File(VideoPadFile): frame_selector=FrameSelector(selection_style='all')): path = self.make_path(directory=directory, extension=extension) - hdf_file = h5py.File(path) + + with HDF5File(path) as f: + return FrameContainer(hdf5=f) + + ''' + hdf_file = h5py.File(path, "r") fc = FrameContainer() frame_keys = list(hdf_file.keys()) @@ -49,7 +54,7 @@ class File(VideoPadFile): hdf_file.close() return fc - + ''' #------------------------------------------------------------------------------ @@ -97,22 +102,6 @@ class MLFPDatabase(FileListPadDatabase): """ Returns annotations for a given file object ``f``. Annotations must be precomputed. - - **Parameters:** - - ``f`` : :py:class:`object` - An instance of file object defined above. - - **Returns:** - - ``annotations`` : :py:class:`dict` - A dictionary containing annotations for - each frame in the video. - Dictionary structure: - ``annotations = {'1': frame1_dict, '2': frame1_dict, ...}``. - Where - ``frameN_dict`` contains coordinates of the - face bounding box and landmarks in frame N. """ if self.annotation_directory is None: @@ -132,7 +121,9 @@ class MLFPDatabase(FileListPadDatabase): return annotations else: - logger.warning("Annotation file for %s does not exist. (Overall path: %s)", f.path, file_path) + logger.warning("Annotation file for %s does not exist.\ + (Overall path: %s)", f.path, file_path) + return None #------------------------------------------------------------------------------ diff --git a/bob/paper/nir_patch_pooling/extractor/patch_pooling_cnn.py b/bob/paper/nir_patch_pooling/extractor/patch_pooling_cnn.py index e98cb5a17177cd79f3c61f769a06c146f1d01e3d..0cc37dde0c416b18da5f5247d6303845957c9b49 100644 --- a/bob/paper/nir_patch_pooling/extractor/patch_pooling_cnn.py +++ b/bob/paper/nir_patch_pooling/extractor/patch_pooling_cnn.py @@ -28,36 +28,41 @@ class PatchPoolingCNN(Extractor): convolutional layer of LightCNN9 (MFM5 layer). """ - def __init__(self, model_file=None, num_classes=79077): + def __init__(self, model_file=None, patch_stride=4, num_classes=79077): Extractor.__init__(self, skip_extractor_training=True) self.network = LightCNN9Patch() + self.patch_stride = patch_stride # load the model into network. cp = torch.load(model_file, map_location="cpu") + cp = cp["state_dict"] # checked if pre-trained model was saved using nn.DataParallel + saved_with_nn_parallel = any("module" in x for x in list(cp.keys())) + + ''' saved_with_data_parallel = False - for k, v in cp["state_dict"].items(): + for k, v in cp.items(): if("module" in k): saved_with_data_parallel = True break + ''' # if DataParallel format, remove module term - if(saved_with_data_parallel): - if("state_dict" in cp): - - from collections import OrderedDict - new_state_dict = OrderedDict() + if(saved_with_nn_parallel): + from collections import OrderedDict + new_state_dict = OrderedDict() - for k, v in cp["state_dict"].items(): - name = k[7:] - new_state_dict[name] = v + for k, v in cp.items(): + k_new = k[7:] + new_state_dict[k_new] = v - self.network.load_state_dict(new_state_dict) + self.network.load_state_dict(new_state_dict) + else: - self.network.load_state_dict(cp["state_dict"]) + self.network.load_state_dict(cp) self.network.eval() @@ -86,38 +91,34 @@ class PatchPoolingCNN(Extractor): # torchvision.transforms expect a numpy array of size HxWxC pil_image = Image.fromarray(image.astype(np.uint8)) input_image = self.data_transform(pil_image) - input_image = input_image.unsqueeze(0) - input_image = input_image.float() + + input_image = input_image.unsqueeze(0).float() + input_image = Variable(input_image) # obtain the features (to be pooled) from forward pass of network - _ , features = self.network.forward(Variable(input_image)) + _ , features = self.network(input_image) # pool features through patch-level processing features = self.conv_to_patch(features) features = features.data.numpy().flatten() + return features.astype(np.float64) #------------------------------------------------------------------------------ def conv_to_patch(self, features): - # parameters for the patch conversion - stride = 4 # features.shape[2]/4 - - # for debugging - # idx = 0 - # num_patch = features.shape[2]/stride - + stride = self.patch_stride pooled_features = torch.zeros(1, stride*stride*features.shape[1]) # obtain patches by tesselation of feature maps # pool linearized version of individual patches for i in range(0, features.shape[2], stride): for j in range(0, features.shape[3], stride): + feat_temp = features[:, :, i:i+stride, j:j+stride] feat_temp = feat_temp.contiguous().view(feat_temp.size(0), -1) pooled_features += feat_temp - # idx += 1 # normalize the vector of pooled features pooled_features = pooled_features/stride/stride diff --git a/bob/paper/nir_patch_pooling/script/annotate_database.py b/bob/paper/nir_patch_pooling/script/annotate_database.py index 8aec28e662930865854ff2f6c2642ee5364dcb9e..5ab78009fdf1d1d8a9698a73b79e9c1728b0a6d8 100755 --- a/bob/paper/nir_patch_pooling/script/annotate_database.py +++ b/bob/paper/nir_patch_pooling/script/annotate_database.py @@ -47,7 +47,7 @@ class Annotator: #------------------------------------------------------------------------------ - def normalize_image(self, image, n_sigma=3.0): # or 4.0 + def normalize_image(self, image, n_sigma=3.0): assert(len(image.shape)==2) @@ -73,15 +73,9 @@ class Annotator: #------------------------------------------------------------------------------ def process(self, fc): - - """ - fc: FrameContainer - returns: - annotations: dictionary where each frame index is a key. - """ - + annotations = {} - prev_index = None + prev_valid_index = None for index, frame, _ in fc: @@ -94,10 +88,10 @@ class Annotator: logger.error("\tException: {}".format(e)) # copy annotations of previous frame - if (prev_index is not None): - - frame_annotations = annotations[prev_index] - logger.warning("\tCopying annotations of previous frame") + if (prev_valid_index is not None): + + frame_annotations = annotations[prev_valid_index] + logger.warning("\tCopying annotations of valid previous frame") else: @@ -105,7 +99,7 @@ class Annotator: logger.warning("\tSetting empty annotations") annotations[str(index)] = frame_annotations - prev_index = str(index) + prev_valid_index = str(index) return annotations diff --git a/bob/paper/nir_patch_pooling/script/convert_mlfp_database.py b/bob/paper/nir_patch_pooling/script/convert_mlfp_database.py index 1148ac4816d7ba72d6abb48b57b4dbed88a66291..c6d4f80279cf8041e5904e167c18c8b182912d9f 100755 --- a/bob/paper/nir_patch_pooling/script/convert_mlfp_database.py +++ b/bob/paper/nir_patch_pooling/script/convert_mlfp_database.py @@ -48,6 +48,7 @@ class MLFPConvertor: file_list = [] for dirpath, dirs, files in os.walk(db_directory): for name in files: + file_path = os.path.join(dirpath, name) file_path = file_path[43:] if ("Infrared.mat" in file_path): @@ -57,7 +58,7 @@ class MLFPConvertor: #------------------------------------------------------------------------------ - def normalize_image(self, image, n_sigma=4.0): # 3.0 or 4.0 + def normalize_image(self, image, n_sigma=4.0): assert(len(image.shape)==2)