""" Crop images to prepare for training """ import click # from PIL import Image, ImageOps, ImageFilter, ImageDraw from app.settings import types from app.utils import click_utils from app.settings import app_cfg as cfg color_filters = {'color': 1, 'gray': 2, 'all': 3} @click.command() @click.option('-i', '--input', 'opt_fp_in', required=True, help='Input CSV (eg image_files.csv)') @click.option('-m', '--media', 'opt_dir_media', required=True, help='Input media directory') @click.option('-o', '--output', 'opt_fp_out', required=True, help='Output CSV') @click.option('--size', 'opt_size', type=(int, int), default=(300, 300), help='Output image size') @click.option('-t', '--detector-type', 'opt_detector_type', type=cfg.FaceDetectNetVar, default=click_utils.get_default(types.FaceDetectNet.DLIB_CNN), help=click_utils.show_help(types.FaceDetectNet)) @click.option('-g', '--gpu', 'opt_gpu', default=0, help='GPU index') @click.option('--conf', 'opt_conf_thresh', default=0.85, type=click.FloatRange(0,1), help='Confidence minimum threshold') @click.option('-p', '--pyramids', 'opt_pyramids', default=0, type=click.IntRange(0,4), help='Number pyramids to upscale for DLIB detectors') @click.option('--slice', 'opt_slice', type=(int, int), default=(None, None), help='Slice list of files') @click.option('--display/--no-display', 'opt_display', is_flag=True, default=False, help='Display detections to debug') @click.option('-f', '--force', 'opt_force', is_flag=True, help='Force overwrite file') @click.option('--color', 'opt_color_filter', type=click.Choice(color_filters.keys()), default='all', help='Filter to keep color or grayscale images (color = keep color') @click.option('--largest', 'opt_largest', is_flag=True, help='Only keep largest face') @click.pass_context def cli(ctx, opt_fp_in, opt_dir_media, opt_fp_out, opt_size, opt_detector_type, opt_gpu, opt_conf_thresh, opt_pyramids, opt_slice, opt_display, opt_force, opt_color_filter, opt_largest): """Converts frames with faces to CSV of ROIs""" 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.utils import logger_utils, file_utils, im_utils from app.processors import face_detector # ------------------------------------------------- # init here log = logger_utils.Logger.getLogger() if not opt_force and Path(opt_fp_out).exists(): log.error('File exists. Use "-f / --force" to overwite') return if opt_detector_type == types.FaceDetectNet.CVDNN: detector = face_detector.DetectorCVDNN() elif opt_detector_type == types.FaceDetectNet.DLIB_CNN: detector = face_detector.DetectorDLIBCNN(opt_gpu) elif opt_detector_type == types.FaceDetectNet.DLIB_HOG: detector = face_detector.DetectorDLIBHOG() elif opt_detector_type == types.FaceDetectNet.MTCNN: detector = face_detector.DetectorMTCNN() elif opt_detector_type == types.FaceDetectNet.HAAR: log.error('{} not yet implemented'.format(opt_detector_type.name)) return # ------------------------------------------------- # process here color_filter = color_filters[opt_color_filter] # get list of files to process df_files = pd.read_csv(opt_fp_in).set_index('index') if opt_slice: df_files = df_files[opt_slice[0]:opt_slice[1]] log.debug('processing {:,} files'.format(len(df_files))) data = [] for df_file in tqdm(df_files.itertuples(), total=len(df_files)): fp_im = join(opt_dir_media, str(df_file.subdir), f'{df_file.fn}.{df_file.ext}') im = cv.imread(fp_im) # filter out color or grayscale iamges if color_filter != color_filters['all']: try: is_gray = im_utils.is_grayscale(im) if is_gray and color_filter != color_filters['gray']: log.debug('Skipping grayscale image: {}'.format(fp_im)) continue except Exception as e: log.error('Could not check grayscale: {}'.format(fp_im)) continue try: bboxes = detector.detect(im, size=opt_size, pyramids=opt_pyramids, largest=opt_largest) except Exception as e: log.error('could not detect: {}'.format(fp_im)) log.error('{}'.format(e)) continue for bbox in bboxes: roi = { 'image_index': int(df_file.Index), 'x': bbox.x, 'y': bbox.y, 'w': bbox.w, 'h': bbox.h, 'image_width': im.shape[1], 'image_height': im.shape[0]} data.append(roi) # debug display if opt_display and len(bboxes): bbox_dim = bbox.to_dim(im.shape[:2][::-1]) # w,h im_md = im_utils.resize(im, width=min(1200, opt_size[0])) for bbox in bboxes: bbox_dim = bbox.to_dim(im_md.shape[:2][::-1]) cv.rectangle(im_md, bbox_dim.pt_tl, bbox_dim.pt_br, (0,255,0), 3) cv.imshow('', im_md) while True: k = cv.waitKey(1) & 0xFF if k == 27 or k == ord('q'): # ESC cv.destroyAllWindows() sys.exit() elif k != 255: # any key to continue break # save date file_utils.mkdirs(opt_fp_out) df = pd.DataFrame.from_dict(data) df.index.name = 'index' df.to_csv(opt_fp_out)