""" Converts ROIs to face vector NB: the VGG Face2 extractor should be used with MTCNN ROIs (not square) the DLIB face extractor should be used with DLIB ROIs (square) see https://github.com/ox-vgg/vgg_face2 for TAR@FAR """ import click from app.settings import types from app.utils import click_utils from app.settings import app_cfg as cfg @click.command() @click.option('-o', '--output', 'opt_fp_out', default=None, help='Override enum output filename CSV') @click.option('-m', '--media', 'opt_dir_media', default=None, help='Override enum media directory') @click.option('--store', 'opt_data_store', type=cfg.DataStoreVar, default=click_utils.get_default(types.DataStore.HDD), show_default=True, help=click_utils.show_help(types.Dataset)) @click.option('--dataset', 'opt_dataset', type=cfg.DatasetVar, required=True, show_default=True, help=click_utils.show_help(types.Dataset)) @click.option('--size', 'opt_size', type=(int, int), default=cfg.DEFAULT_SIZE_FACE_DETECT, help='Output image size') @click.option('-e', '--extractor', 'opt_extractor', default=click_utils.get_default(types.FaceExtractor.VGG), type=cfg.FaceExtractorVar, help='Type of extractor framework/network to use') @click.option('-j', '--jitters', 'opt_jitters', default=cfg.DLIB_FACEREC_JITTERS, help='Number of jitters (only for dlib') @click.option('-p', '--padding', 'opt_padding', default=cfg.FACEREC_PADDING, help='Percentage ROI padding') @click.option('--slice', 'opt_slice', type=(int, int), default=(None, None), help='Slice list of files') @click.option('-f', '--force', 'opt_force', is_flag=True, help='Force overwrite file') @click.option('-g', '--gpu', 'opt_gpu', default=0, help='GPU index') @click.pass_context def cli(ctx, opt_fp_out, opt_dir_media, opt_data_store, opt_dataset, opt_size, opt_extractor, opt_slice, opt_force, opt_gpu, opt_jitters, opt_padding): """Converts face ROIs to vectors""" import sys import os from os.path import join from pathlib import Path from glob import glob from tqdm import tqdm import numpy as np import dlib # must keep a local reference for dlib import cv2 as cv import pandas as pd from app.models.bbox import BBox from app.models.data_store import DataStore from app.utils import logger_utils, file_utils, im_utils from app.processors import face_extractor # ------------------------------------------------- # init here log = logger_utils.Logger.getLogger() # set data_store data_store = DataStore(opt_data_store, opt_dataset) # get filepath out fp_out = data_store.metadata(types.Metadata.FACE_VECTOR) if opt_fp_out is None else opt_fp_out if not opt_force and Path(fp_out).exists(): log.error('File exists. Use "-f / --force" to overwite') return # init face processors if opt_extractor == types.FaceExtractor.DLIB: log.debug('set dlib') extractor = face_extractor.ExtractorDLIB(gpu=opt_gpu, jitters=opt_jitters) elif opt_extractor == types.FaceExtractor.VGG: extractor = face_extractor.ExtractorVGG() # load data fp_record = data_store.metadata(types.Metadata.FILE_RECORD) df_record = pd.read_csv(fp_record, dtype={'fn':str}).set_index('index') fp_roi = data_store.metadata(types.Metadata.FACE_ROI) df_roi = pd.read_csv(fp_roi).set_index('index') if opt_slice: df_roi = df_roi[opt_slice[0]:opt_slice[1]] # ------------------------------------------------- # process images df_img_groups = df_roi.groupby('record_index') log.debug('processing {:,} groups'.format(len(df_img_groups))) vecs = [] for record_index, df_img_group in tqdm(df_img_groups): # make fp ds_record = df_record.iloc[record_index] fp_im = data_store.face(ds_record.subdir, ds_record.fn, ds_record.ext) im = cv.imread(fp_im) for roi_index, df_img in df_img_group.iterrows(): # get bbox x, y, w, h = df_img.x, df_img.y, df_img.w, df_img.h dim = (ds_record.width, ds_record.height) # get face vector bbox = BBox.from_xywh(x, y, w, h) # norm # compute vec vec = extractor.extract(im, bbox) # use normalized BBox vec_str = extractor.to_str(vec) vec_obj = {'vec':vec_str, 'roi_index': roi_index, 'record_index':record_index} vecs.append(vec_obj) # ------------------------------------------------- # save data # create DataFrame and save to CSV df = pd.DataFrame.from_dict(vecs) df.index.name = 'index' file_utils.mkdirs(fp_out) df.to_csv(fp_out) # save script file_utils.write_text(' '.join(sys.argv), '{}.sh'.format(fp_out))