From 65cb506ca182272e2701136097fd00c55dc6bd69 Mon Sep 17 00:00:00 2001 From: adamhrv Date: Wed, 16 Jan 2019 13:30:16 +0100 Subject: change bbox to norm, refine face extractor --- megapixels/commands/demo/face_3ddfa.py | 85 +++++++------- megapixels/commands/demo/face_age_gender.py | 31 ++--- megapixels/commands/demo/face_beauty.py | 12 +- megapixels/commands/demo/face_detect.py | 85 ++++++++++++++ megapixels/commands/demo/face_detection.py | 126 --------------------- megapixels/commands/demo/face_landmarks_2d.py | 157 +++----------------------- megapixels/commands/demo/face_landmarks_3d.py | 82 +++----------- megapixels/commands/demo/face_pose.py | 25 ++-- megapixels/commands/demo/face_search.py | 40 ++++--- megapixels/commands/demo/face_vector.py | 28 +++-- 10 files changed, 226 insertions(+), 445 deletions(-) create mode 100644 megapixels/commands/demo/face_detect.py delete mode 100644 megapixels/commands/demo/face_detection.py (limited to 'megapixels/commands/demo') diff --git a/megapixels/commands/demo/face_3ddfa.py b/megapixels/commands/demo/face_3ddfa.py index 6182aeb6..90359159 100644 --- a/megapixels/commands/demo/face_3ddfa.py +++ b/megapixels/commands/demo/face_3ddfa.py @@ -1,7 +1,7 @@ ''' Combines 3D face mode + rendering -https://github.com/cleardusk/3DDFA -https://github.com/YadiraF/face3d +https://github.com/cleardusk/3DDFA --> 3d landmarks +https://github.com/YadiraF/face3d --> render 3D with lighting as 2.5d image ''' import click @@ -13,8 +13,8 @@ from app.settings import app_cfg as cfg @click.command() @click.option('-i', '--input', 'opt_fp_in', default=None, required=True, help='Image filepath') -@click.option('-o', '--output', 'opt_fp_out', default=None, - help='GIF output path') +@click.option('-o', '--output', 'opt_dir_out', default=None, + help='Directory for output files') @click.option('--size', 'opt_size', type=(int, int), default=(300, 300), help='Output image size') @@ -27,11 +27,13 @@ from app.settings import app_cfg as cfg @click.option('--size', 'opt_render_dim', type=(int, int), default=(512, 512), help='2.5D render image size') -@click.option('--display/--no-display', 'opt_display', is_flag=True, default=False, +@click.option('--display/--no-display', 'opt_display', is_flag=True, default=True, help='Display detections to debug') +@click.option('--save/--no-save', 'opt_save', is_flag=True, default=True, + help='Save output images/files') @click.pass_context -def cli(ctx, opt_fp_in, opt_fp_out, opt_gpu, opt_bbox_init, - opt_size, opt_render_dim, opt_force, opt_display): +def cli(ctx, opt_fp_in, opt_dir_out, opt_gpu, opt_bbox_init, + opt_size, opt_render_dim, opt_force, opt_display, opt_save): """3D face demo""" import sys @@ -58,6 +60,7 @@ def cli(ctx, opt_fp_in, opt_fp_out, opt_gpu, opt_bbox_init, import scipy.io as sio sys.path.append(join(Path.cwd().parent, '3rdparty')) + # git clone https://github.com/cleardusk/3DDFA 3rdparty/d3ddfa # change name of 3DDFA to d3DDFA because can't start with number from d3DDFA import mobilenet_v1 from d3DDFA.utils.ddfa import ToTensorGjz, NormalizeGjz, str2bool @@ -70,7 +73,7 @@ def cli(ctx, opt_fp_in, opt_fp_out, opt_gpu, opt_bbox_init, from d3DDFA.utils.render import get_depths_image, cget_depths_image, cpncc from d3DDFA.utils import paf as d3dfa_paf_utils - # https://github.com/YadiraF/face3d + # git clone https://github.com/YadiraF/face3d 3rdparty/face3d # compile cython module in face3d/mesh/cython/ python setup.py build_ext -i from face3d.face3d import mesh as face3d_mesh @@ -82,13 +85,11 @@ def cli(ctx, opt_fp_in, opt_fp_out, opt_gpu, opt_bbox_init, fpp_in = Path(opt_fp_in) im = cv.imread(opt_fp_in) - #im = im_utils.resize(im_orig, width=opt_size[0], height=opt_size[1]) - # im = im_orig.copy() # ---------------------------------------------------------------------------- # detect face - face_detector = face_detector.DetectorDLIBCNN(gpu=opt_gpu) # -1 for CPU + face_detector = face_detector.DetectorCVDNN() # -1 for CPU bboxes = face_detector.detect(im, largest=True) bbox = bboxes[0] dim = im.shape[:2][::-1] @@ -165,7 +166,6 @@ def cli(ctx, opt_fp_in, opt_fp_out, opt_gpu, opt_bbox_init, # dense face 3d vertices vertices = d3dfa_utils.predict_dense(param, roi_box) vertices_lst.append(vertices) - log.info(f'generated 3d data in: {(time.time() - st):.2f}s') # filepath helper function @@ -183,28 +183,20 @@ def cli(ctx, opt_fp_in, opt_fp_out, opt_gpu, opt_bbox_init, sio.savemat(fp_mat_3df, {'vertices': vertices, 'colors': colors, 'triangles': triangles}) # save PAF - #fp_paf = to_fp(fpp_in, 'jpg', suffix='paf') - #opt_paf_size = 3 # PAF feature kernel size - #im_paf = d3dfa_paf_utils.gen_img_paf(img_crop=im_crop, param=param, kernel_size=opt_paf_size) - #cv.imwrite(fp_paf, im_paf) + im_paf = d3dfa_paf_utils.gen_img_paf(img_crop=im_crop, param=param, kernel_size=3) # save pose image # P, pose = parse_pose(param) # Camera matrix (without scale), and pose (yaw, pitch, roll, to verify) - img_pose = draw_utils.plot_pose_box(im, Ps, pts_res) - fp_pose = to_fp(fpp_in, 'jpg', suffix='pose') - cv.imwrite(fp_pose, img_pose) + im_pose = draw_utils.plot_pose_box(im, Ps, pts_res) # save depth image - fp_depth = to_fp(fpp_in, 'png', suffix='depth') # depths_img = get_depths_image(im, vertices_lst, tri-1) # python version im_depth = cget_depths_image(im, vertices_lst, triangles - 1) # cython version - cv.imwrite(fp_depth, im_depth) # save pncc image - fp_pose = to_fp(fpp_in, 'png', suffix='pncc') pncc_feature = cpncc(im, vertices_lst, triangles - 1) # cython version - cv.imwrite(fp_pose, pncc_feature[:, :, ::-1]) # cv.imwrite will swap RGB -> BGR + im_pncc = pncc_feature[:, :, ::-1] # swap BGR # save .ply #fp_ply = to_fp(fpp_in, 'ply') @@ -228,8 +220,6 @@ def cli(ctx, opt_fp_in, opt_fp_out, opt_gpu, opt_bbox_init, # save obj colors = d3dfa_utils.get_colors(im, vertices_orig) - fp_obj = to_fp(fpp_in, 'obj') - write_obj_with_colors(fp_obj, vertices_orig, triangles, colors) #fp_landmarks = to_fp(fpp_in, 'jpg', suffix='3DDFA') # show_flg? @@ -276,30 +266,39 @@ def cli(ctx, opt_fp_in, opt_fp_out, opt_gpu, opt_bbox_init, vertices_proj = face3d_mesh.transform.orthographic_project(vertices_cam) # ------------------------------------------------------------------------- - # render 2D image + # render 2D images w = h = max(opt_render_dim) vertices_im = face3d_mesh.transform.to_image(vertices_proj, h, w) - rendering = face3d_mesh.render.render_colors(vertices_im, triangles, colors_lit, h, w) - - cv.imshow('', rendering) - display_utils.handle_keyboard() + im_render = face3d_mesh.render.render_colors(vertices_im, triangles, colors_lit, h, w) + im_render = (255* im_render).astype(np.uint8) + im_pncc = im_pncc.astype(np.uint8) + im_depth = im_depth.astype(np.uint8) + im_paf = im_paf.astype(np.uint8) # ---------------------------------------------------------------------------- # save - if opt_fp_out: - # save pose only - fpp_out = Path(opt_fp_out) + if opt_save: + fpp_out = Path(opt_dir_out) if opt_dir_out is not None else Path(opt_fp_in).parent + fpp_in = Path(opt_fp_in) + fp_out = join(fpp_out, f'{fpp_in.stem}_render.png') + cv.imwrite(fp_out, im_render) + + fp_out = join(fpp_out, f'{fpp_in.stem}_pose.png') + cv.imwrite(fp_out, im_pose) - fp_out = join(fpp_out.parent, f'{fpp_out.stem}_real{fpp_out.suffix}') - cv.imwrite(fp_out, im_age_real) + fp_out = join(fpp_out, f'{fpp_in.stem}_depth.png') + cv.imwrite(fp_out, im_depth) + + fp_out = join(fpp_out, f'{fpp_in.stem}_pncc.png') + cv.imwrite(fp_out, im_pncc) - fp_out = join(fpp_out.parent, f'{fpp_out.stem}_apparent{fpp_out.suffix}') - cv.imwrite(fp_out, im_age_apparent) + fp_out = join(fpp_out, f'{fpp_in.stem}_paf.png') + cv.imwrite(fp_out, im_paf) - fp_out = join(fpp_out.parent, f'{fpp_out.stem}_gender{fpp_out.suffix}') - cv.imwrite(fp_out, im_age_apparent) + fp_out = join(fpp_out, f'{fpp_in.stem}.obj') + write_obj_with_colors(fp_out, vertices_orig, triangles, colors) # ---------------------------------------------------------------------------- @@ -307,8 +306,10 @@ def cli(ctx, opt_fp_in, opt_fp_out, opt_gpu, opt_bbox_init, if opt_display: # show all images here - cv.imshow('real', im_age_real) - cv.imshow('apparent', im_age_apparent) - cv.imshow('gender', im_gender) + cv.imshow('3d', im_render) + cv.imshow('depth', im_depth) + cv.imshow('pncc', im_pncc) + cv.imshow('pose', im_pose) + cv.imshow('paf', im_paf) display_utils.handle_keyboard() diff --git a/megapixels/commands/demo/face_age_gender.py b/megapixels/commands/demo/face_age_gender.py index c74f1e45..c4f09c13 100644 --- a/megapixels/commands/demo/face_age_gender.py +++ b/megapixels/commands/demo/face_age_gender.py @@ -17,7 +17,7 @@ from app.settings import app_cfg as cfg help='GPU index') @click.option('-f', '--force', 'opt_force', is_flag=True, help='Force overwrite file') -@click.option('--display/--no-display', 'opt_display', is_flag=True, default=False, +@click.option('--display/--no-display', 'opt_display', is_flag=True, default=True, help='Display detections to debug') @click.pass_context def cli(ctx, opt_fp_in, opt_fp_out, opt_gpu, opt_size, opt_force, opt_display): @@ -52,12 +52,12 @@ def cli(ctx, opt_fp_in, opt_fp_out, opt_gpu, opt_size, opt_force, opt_display): # ---------------------------------------------------------------------------- # detect face - face_detector = face_detector.DetectorDLIBCNN(gpu=opt_gpu) # -1 for CPU + face_detector = face_detector.DetectorCVDNN() bboxes = face_detector.detect(im_resized, largest=True) - bbox = bboxes[0] + bbox_norm = bboxes[0] dim = im_resized.shape[:2][::-1] - bbox_dim = bbox.to_dim(dim) - if not bbox: + bbox_dim = bbox_norm.to_dim(dim) + if not bbox_norm: log.error('no face detected') return else: @@ -70,21 +70,24 @@ def cli(ctx, opt_fp_in, opt_fp_out, opt_gpu, opt_size, opt_force, opt_display): # real age_real_predictor = face_age_gender.FaceAgeReal() st = time.time() - age_real = age_real_predictor.predict(im_resized, bbox_dim) + age_real = age_real_predictor.predict(im_resized, bbox_norm) log.info(f'age real took: {(time.time()-st)/1000:.5f}s') # apparent age_apparent_predictor = face_age_gender.FaceAgeApparent() st = time.time() - age_apparent = age_apparent_predictor.predict(im_resized, bbox_dim) + age_apparent = age_apparent_predictor.predict(im_resized, bbox_norm) log.info(f'age apparent took: {(time.time()-st)/1000:.5f}s') # gender gender_predictor = face_age_gender.FaceGender() st = time.time() - gender = gender_predictor.predict(im_resized, bbox_dim) + gender = gender_predictor.predict(im_resized, bbox_norm) log.info(f'gender took: {(time.time()-st)/1000:.5f}s') + # ethnicity + # TODO + # ---------------------------------------------------------------------------- # output @@ -99,21 +102,21 @@ def cli(ctx, opt_fp_in, opt_fp_out, opt_gpu, opt_size, opt_force, opt_display): # draw real age im_age_real = im_resized.copy() - draw_utils.draw_bbox(im_age_real, bbox_dim) + im_age_real = draw_utils.draw_bbox(im_age_real, bbox_norm) txt = f'{(age_real):.2f}' - draw_utils.draw_text(im_age_real, bbox_dim.pt_tl, txt) + im_age_real = draw_utils.draw_text(im_age_real, bbox_norm.pt_tl, txt) # apparent age im_age_apparent = im_resized.copy() - draw_utils.draw_bbox(im_age_apparent, bbox_dim) + im_age_apparent = draw_utils.draw_bbox(im_age_apparent, bbox_norm) txt = f'{(age_apparent):.2f}' - draw_utils.draw_text(im_age_apparent, bbox_dim.pt_tl, txt) + im_age_apparent = draw_utils.draw_text(im_age_apparent, bbox_norm.pt_tl, txt) # gender im_gender = im_resized.copy() - draw_utils.draw_bbox(im_age_apparent, bbox_dim) + im_gender = draw_utils.draw_bbox(im_gender, bbox_norm) txt = f"M: {gender['m']:.2f}, F: {gender['f']:.2f}" - draw_utils.draw_text(im_gender, (10, dim[1]-20), txt) + im_gender = draw_utils.draw_text(im_gender, (.1, .9), txt) # ---------------------------------------------------------------------------- diff --git a/megapixels/commands/demo/face_beauty.py b/megapixels/commands/demo/face_beauty.py index d31c5cee..45643c61 100644 --- a/megapixels/commands/demo/face_beauty.py +++ b/megapixels/commands/demo/face_beauty.py @@ -66,10 +66,10 @@ def cli(ctx, opt_fp_in, opt_fp_out, opt_gpu, opt_size, opt_force, opt_display): face_detector = face_detector.DetectorDLIBCNN(gpu=opt_gpu) # -1 for CPU bboxes = face_detector.detect(im_resized, largest=True) - bbox = bboxes[0] + bbox_norm = bboxes[0] dim = im_resized.shape[:2][::-1] - bbox_dim = bbox.to_dim(dim) - if not bbox: + bbox_dim = bbox_norm.to_dim(dim) + if not bbox_norm: log.error('no face detected') return else: @@ -78,7 +78,7 @@ def cli(ctx, opt_fp_in, opt_fp_out, opt_gpu, opt_size, opt_force, opt_display): # ---------------------------------------------------------------------------- # beauty - beauty_score = beauty_predictor.beauty(im_resized, bbox_dim) + beauty_score = beauty_predictor.beauty(im_resized, bbox_norm) # ---------------------------------------------------------------------------- @@ -93,9 +93,9 @@ def cli(ctx, opt_fp_in, opt_fp_out, opt_gpu, opt_size, opt_force, opt_display): # draw 2d landmarks im_beauty = im_resized.copy() - draw_utils.draw_bbox(im_beauty, bbox_dim) + im_beauty = draw_utils.draw_bbox(im_beauty, bbox_dim) txt = f'Beauty score: {(100*beauty_score):.2f}' - draw_utils.draw_text(im_beauty, bbox_dim.pt_tl, txt) + im_beauty = draw_utils.draw_text(im_beauty, bbox_dim.pt_tl, txt) # ---------------------------------------------------------------------------- diff --git a/megapixels/commands/demo/face_detect.py b/megapixels/commands/demo/face_detect.py new file mode 100644 index 00000000..b92db7cb --- /dev/null +++ b/megapixels/commands/demo/face_detect.py @@ -0,0 +1,85 @@ +""" +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 + + +@click.command() +@click.option('-i', '--input', 'opt_fp_in', default=None, required=True, + help='Image filepath') +@click.option('-o', '--output', 'opt_fp_out', default=None, + help='GIF output path') +@click.option('--size', 'opt_size', + type=(int, int), default=(300, 300), + help='Output image size') +@click.option('-g', '--gpu', 'opt_gpu', default=0, + help='GPU index') +@click.option('-f', '--force', 'opt_force', is_flag=True, + help='Force overwrite file') +@click.option('--display/--no-display', 'opt_display', is_flag=True, default=False, + help='Display detections to debug') +@click.pass_context +def cli(ctx, opt_fp_in, opt_fp_out, opt_gpu, opt_size, opt_force, opt_display): + """Face detector demo""" + + import sys + import os + from os.path import join + from pathlib import Path + import time + + from tqdm import tqdm + import numpy as np + import pandas as pd + import cv2 as cv + import dlib + + from app.utils import logger_utils, file_utils, im_utils, display_utils, draw_utils + from app.utils import plot_utils + from app.processors import face_detector, face_landmarks + from app.models.data_store import DataStore + + + log = logger_utils.Logger.getLogger() + + + # ------------------------------------------------- + # load image + + im = cv.imread(opt_fp_in) + im_resized = im_utils.resize(im, width=opt_size[0], height=opt_size[1]) + + + # ---------------------------------------------------------------------------- + # detect face + + face_detector = face_detector.DetectorCVDNN() + bboxes = face_detector.detect(im_resized, largest=True) + if not bboxes: + log.error('no face detected') + return + bbox_norm = bboxes[0] + dim = im_resized.shape[:2][::-1] + bbox_dim = bbox_norm.to_dim(dim) + + + # ---------------------------------------------------------------------------- + # draw + + im_face = im_resized.copy() + im_face = draw_utils.draw_bbox(im_face, bbox_norm) + + # ---------------------------------------------------------------------------- + # display + + if opt_display: + + # show all images here + cv.imshow('Face', im_face) + display_utils.handle_keyboard() \ No newline at end of file diff --git a/megapixels/commands/demo/face_detection.py b/megapixels/commands/demo/face_detection.py deleted file mode 100644 index 488cc80d..00000000 --- a/megapixels/commands/demo/face_detection.py +++ /dev/null @@ -1,126 +0,0 @@ -""" -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 - - -@click.command() -@click.option('-i', '--input', 'opt_fp_in', default=None, required=True, - help='Image filepath') -@click.option('-o', '--output', 'opt_fp_out', default=None, - help='GIF output path') -@click.option('--size', 'opt_size', - type=(int, int), default=(300, 300), - help='Output image size') -@click.option('-g', '--gpu', 'opt_gpu', default=0, - help='GPU index') -@click.option('-f', '--force', 'opt_force', is_flag=True, - help='Force overwrite file') -@click.option('--display/--no-display', 'opt_display', is_flag=True, default=False, - help='Display detections to debug') -@click.pass_context -def cli(ctx, opt_fp_in, opt_fp_out, opt_gpu, opt_size, opt_force, opt_display): - """Face detector demo""" - - import sys - import os - from os.path import join - from pathlib import Path - import time - - from tqdm import tqdm - import numpy as np - import pandas as pd - import cv2 as cv - import dlib - - from app.utils import logger_utils, file_utils, im_utils, display_utils, draw_utils - from app.utils import plot_utils - from app.processors import face_detector, face_landmarks - from app.models.data_store import DataStore - - - log = logger_utils.Logger.getLogger() - - - # ------------------------------------------------- - # load image - - im = cv.imread(opt_fp_in) - im_resized = im_utils.resize(im, width=opt_size[0], height=opt_size[1]) - - - # ---------------------------------------------------------------------------- - # detect face - - face_detector = face_detector.DetectorDLIBCNN(gpu=opt_gpu) # -1 for CPU - bboxes = face_detector.detect(im_resized, largest=True) - bbox = bboxes[0] - dim = im_resized.shape[:2][::-1] - bbox_dim = bbox.to_dim(dim) - if not bbox: - log.error('no face detected') - return - - - # ---------------------------------------------------------------------------- - # generate 68 point landmarks using dlib - - from app.processors import face_landmarks - landmark_detector_2d_68 = face_landmarks.Dlib2D_68() - points_2d_68 = landmark_detector_2d_68.landmarks(im_resized, bbox_dim) - - - # ---------------------------------------------------------------------------- - # generate pose from 68 point 2D landmarks - - from app.processors import face_pose - pose_detector = face_pose.FacePoseDLIB() - pose_data = pose_detector.pose(points_2d_68, dim) - - # ---------------------------------------------------------------------------- - # output - - log.info(f'Face coords: {bbox_dim} face') - log.info(f'pitch: {pose_data["pitch"]}, roll: {pose_data["roll"]}, yaw: {pose_data["yaw"]}') - - - # ---------------------------------------------------------------------------- - # draw - - # draw 2d landmarks - im_landmarks_2d_68 = im_resized.copy() - draw_utils.draw_landmarks2D(im_landmarks_2d_68, points_2d_68) - draw_utils.draw_bbox(im_landmarks_2d_68, bbox_dim) - - # draw pose - im_pose = im_resized.copy() - draw_utils.draw_pose(im_pose, pose_data['point_nose'], pose_data['points']) - draw_utils.draw_degrees(im_pose, pose_data) - - - # ---------------------------------------------------------------------------- - # save - - if opt_fp_out: - # save pose only - cv.imwrite(opt_fp_out, im_pose) - - - # ---------------------------------------------------------------------------- - # display - - if opt_display: - - - # show all images here - cv.imshow('Original', im_resized) - cv.imshow('2D 68PT Landmarks', im_landmarks_2d_68) - cv.imshow('Pose', im_pose) - display_utils.handle_keyboard() \ No newline at end of file diff --git a/megapixels/commands/demo/face_landmarks_2d.py b/megapixels/commands/demo/face_landmarks_2d.py index 22e09297..145a12a6 100644 --- a/megapixels/commands/demo/face_landmarks_2d.py +++ b/megapixels/commands/demo/face_landmarks_2d.py @@ -3,7 +3,6 @@ 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 @@ -13,26 +12,14 @@ from app.settings import app_cfg as cfg @click.command() @click.option('-i', '--input', 'opt_fp_in', default=None, required=True, help='Image filepath') -@click.option('-o', '--output', 'opt_fp_out', default=None, - help='GIF output path') @click.option('--size', 'opt_size', type=(int, int), default=(300, 300), help='Output image size') -@click.option('--gif-size', 'opt_gif_size', - type=(int, int), default=(480, 480), - help='GIF output size') -@click.option('--gif-frames', 'opt_gif_frames', default=15, - help='GIF frames') -@click.option('-g', '--gpu', 'opt_gpu', default=0, - help='GPU index') -@click.option('-f', '--force', 'opt_force', is_flag=True, - help='Force overwrite file') -@click.option('--display/--no-display', 'opt_display', is_flag=True, default=False, +@click.option('--display/--no-display', 'opt_display', is_flag=True, default=True, help='Display detections to debug') @click.pass_context -def cli(ctx, opt_fp_in, opt_fp_out, opt_gpu, opt_gif_frames, - opt_size, opt_gif_size, opt_force, opt_display): - """Generates 3D landmark animations from CSV files""" +def cli(ctx, opt_fp_in, opt_size, opt_display): + """2D 68-point landmarks""" import sys import os @@ -52,12 +39,6 @@ def cli(ctx, opt_fp_in, opt_fp_out, opt_gpu, opt_gif_frames, from app.utils import plot_utils from app.processors import face_detector, face_landmarks from app.models.data_store import DataStore - - # TOOD add selective testing - opt_run_pose = True - opt_run_2d_68 = True - opt_run_3d_68 = True - opt_run_3d_68 = True # ------------------------------------------------- @@ -66,7 +47,6 @@ def cli(ctx, opt_fp_in, opt_fp_out, opt_gpu, opt_gif_frames, log = logger_utils.Logger.getLogger() - # load image im = cv.imread(opt_fp_in) im_resized = im_utils.resize(im, width=opt_size[0], height=opt_size[1]) @@ -74,146 +54,41 @@ def cli(ctx, opt_fp_in, opt_fp_out, opt_gpu, opt_gif_frames, # ---------------------------------------------------------------------------- # detect face - face_detector = face_detector.DetectorDLIBCNN(gpu=opt_gpu) # -1 for CPU + face_detector = face_detector.DetectorCVDNN() log.info('detecting face...') st = time.time() bboxes = face_detector.detect(im_resized, largest=True) - bbox = bboxes[0] + bbox_norm = bboxes[0] dim = im_resized.shape[:2][::-1] - bbox_dim = bbox.to_dim(dim) - if not bbox: + bbox_dim = bbox_norm.to_dim(dim) + if not bbox_norm: log.error('no face detected') return else: log.info(f'Detected face in {(time.time() - st):.2f}s') - log.info('') - - - # ---------------------------------------------------------------------------- - # detect 3D landmarks - log.info('loading 3D landmark generator files...') - landmark_detector_3d_68 = face_landmarks.FaceAlignment3D_68(gpu=opt_gpu) # -1 for CPU - log.info('generating 3D landmarks...') - st = time.time() - points_3d_68 = landmark_detector_3d_68.landmarks(im_resized, bbox_dim.to_xyxy()) - log.info(f'generated 3D landmarks in {(time.time() - st):.2f}s') - log.info('') - - - # ---------------------------------------------------------------------------- - # generate 3D GIF animation - - log.info('generating 3D animation...') - if not opt_fp_out: - fpp_im = Path(opt_fp_in) - fp_out = join(fpp_im.parent, f'{fpp_im.stem}_anim.gif') - else: - fp_out = opt_fp_out - st = time.time() - plot_utils.generate_3d_landmark_anim(np.array(points_3d_68), fp_out, - size=opt_gif_size, num_frames=opt_gif_frames) - log.info(f'Generated animation in {(time.time() - st):.2f}s') - log.info(f'Saved to: {fp_out}') - log.info('') - - - # ---------------------------------------------------------------------------- - # generate face vectors, only to test if feature extraction works - - log.info('initialize face recognition model...') - from app.processors import face_recognition - face_rec = face_recognition.RecognitionDLIB() - st = time.time() - log.info('generating face vector...') - vec = face_rec.vec(im_resized, bbox_dim) - log.info(f'generated face vector in {(time.time() - st):.2f}s') - log.info('') - # ---------------------------------------------------------------------------- # generate 68 point landmarks using dlib log.info('initializing face landmarks 68 dlib...') - from app.processors import face_landmarks landmark_detector_2d_68 = face_landmarks.Dlib2D_68() log.info('generating 2D 68PT landmarks...') st = time.time() - points_2d_68 = landmark_detector_2d_68.landmarks(im_resized, bbox_dim) + points_norm = landmark_detector_2d_68.landmarks(im_resized, bbox_norm) log.info(f'generated 2D 68PT face landmarks in {(time.time() - st):.2f}s') - log.info('') # ---------------------------------------------------------------------------- - # generate pose from 68 point 2D landmarks - - if opt_run_pose: - log.info('initialize pose...') - from app.processors import face_pose - pose_detector = face_pose.FacePoseDLIB() - log.info('generating pose...') - st = time.time() - pose_data = pose_detector.pose(points_2d_68, dim) - log.info(f'generated pose {(time.time() - st):.2f}s') - log.info('') - - - # x - - - # display + if opt_display: - - # draw bbox - - # draw 3d landmarks - im_landmarks_3d_68 = im_resized.copy() - draw_utils.draw_landmarks3D(im_landmarks_3d_68, points_3d_68) - draw_utils.draw_bbox(im_landmarks_3d_68, bbox_dim) # draw 2d landmarks - im_landmarks_2d_68 = im_resized.copy() - draw_utils.draw_landmarks2D(im_landmarks_2d_68, points_2d_68) - draw_utils.draw_bbox(im_landmarks_2d_68, bbox_dim) - - # draw pose - if opt_run_pose: - im_pose = im_resized.copy() - draw_utils.draw_pose(im_pose, pose_data['point_nose'], pose_data['points']) - draw_utils.draw_degrees(im_pose, pose_data) - - # draw animated GIF - im = Image.open(opt_fp_out) - im_frames = [] - duration = im.info['duration'] - try: - while True: - im.seek(len(im_frames)) - mypalette = im.getpalette() - im.putpalette(mypalette) - im_jpg = Image.new("RGB", im.size) - im_jpg.paste(im) - im_np = im_utils.pil2np(im_jpg.copy()) - im_frames.append(im_np) - except EOFError: - pass # end of GIF sequence - - n_frames = len(im_frames) - frame_number = 0 - - while True: - # show all images here - cv.imshow('Original', im_resized) - cv.imshow('2D 68PT Landmarks', im_landmarks_2d_68) - cv.imshow('3D 68PT Landmarks', im_landmarks_3d_68) - cv.imshow('Pose', im_pose) - cv.imshow('3D 68pt GIF', im_frames[frame_number]) - frame_number = (frame_number + 1) % n_frames - k = cv.waitKey(duration) & 0xFF - if k == 27 or k == ord('q'): # ESC - cv.destroyAllWindows() - sys.exit() - elif k != 255: - # any key to continue - break \ No newline at end of file + im_lmarks = im_resized.copy() + im_lmarks = draw_utils.draw_bbox(im_lmarks, bbox_norm) + im_lmarks = draw_utils.draw_landmarks2D(im_lmarks, points_norm) + + # show all images here + cv.imshow('2D 68PT Landmarks', im_lmarks) + display_utils.handle_keyboard() \ No newline at end of file diff --git a/megapixels/commands/demo/face_landmarks_3d.py b/megapixels/commands/demo/face_landmarks_3d.py index 22e09297..ed5a00d5 100644 --- a/megapixels/commands/demo/face_landmarks_3d.py +++ b/megapixels/commands/demo/face_landmarks_3d.py @@ -3,7 +3,6 @@ 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 @@ -27,7 +26,7 @@ from app.settings import app_cfg as cfg help='GPU index') @click.option('-f', '--force', 'opt_force', is_flag=True, help='Force overwrite file') -@click.option('--display/--no-display', 'opt_display', is_flag=True, default=False, +@click.option('--display/--no-display', 'opt_display', is_flag=True, default=True, help='Display detections to debug') @click.pass_context def cli(ctx, opt_fp_in, opt_fp_out, opt_gpu, opt_gif_frames, @@ -52,12 +51,6 @@ def cli(ctx, opt_fp_in, opt_fp_out, opt_gpu, opt_gif_frames, from app.utils import plot_utils from app.processors import face_detector, face_landmarks from app.models.data_store import DataStore - - # TOOD add selective testing - opt_run_pose = True - opt_run_2d_68 = True - opt_run_3d_68 = True - opt_run_3d_68 = True # ------------------------------------------------- @@ -74,14 +67,14 @@ def cli(ctx, opt_fp_in, opt_fp_out, opt_gpu, opt_gif_frames, # ---------------------------------------------------------------------------- # detect face - face_detector = face_detector.DetectorDLIBCNN(gpu=opt_gpu) # -1 for CPU + face_detector = face_detector.DetectorCVDNN() log.info('detecting face...') st = time.time() bboxes = face_detector.detect(im_resized, largest=True) - bbox = bboxes[0] + bbox_norm = bboxes[0] dim = im_resized.shape[:2][::-1] - bbox_dim = bbox.to_dim(dim) - if not bbox: + bbox_dim = bbox_norm.to_dim(dim) + if not bbox_norm: log.error('no face detected') return else: @@ -96,7 +89,7 @@ def cli(ctx, opt_fp_in, opt_fp_out, opt_gpu, opt_gif_frames, landmark_detector_3d_68 = face_landmarks.FaceAlignment3D_68(gpu=opt_gpu) # -1 for CPU log.info('generating 3D landmarks...') st = time.time() - points_3d_68 = landmark_detector_3d_68.landmarks(im_resized, bbox_dim.to_xyxy()) + points_3d_68 = landmark_detector_3d_68.landmarks(im_resized, bbox_norm) log.info(f'generated 3D landmarks in {(time.time() - st):.2f}s') log.info('') @@ -118,19 +111,6 @@ def cli(ctx, opt_fp_in, opt_fp_out, opt_gpu, opt_gif_frames, log.info('') - # ---------------------------------------------------------------------------- - # generate face vectors, only to test if feature extraction works - - log.info('initialize face recognition model...') - from app.processors import face_recognition - face_rec = face_recognition.RecognitionDLIB() - st = time.time() - log.info('generating face vector...') - vec = face_rec.vec(im_resized, bbox_dim) - log.info(f'generated face vector in {(time.time() - st):.2f}s') - log.info('') - - # ---------------------------------------------------------------------------- # generate 68 point landmarks using dlib @@ -139,54 +119,25 @@ def cli(ctx, opt_fp_in, opt_fp_out, opt_gpu, opt_gif_frames, landmark_detector_2d_68 = face_landmarks.Dlib2D_68() log.info('generating 2D 68PT landmarks...') st = time.time() - points_2d_68 = landmark_detector_2d_68.landmarks(im_resized, bbox_dim) + points_2d_68 = landmark_detector_2d_68.landmarks(im_resized, bbox_norm) log.info(f'generated 2D 68PT face landmarks in {(time.time() - st):.2f}s') log.info('') - # ---------------------------------------------------------------------------- - # generate pose from 68 point 2D landmarks - - if opt_run_pose: - log.info('initialize pose...') - from app.processors import face_pose - pose_detector = face_pose.FacePoseDLIB() - log.info('generating pose...') - st = time.time() - pose_data = pose_detector.pose(points_2d_68, dim) - log.info(f'generated pose {(time.time() - st):.2f}s') - log.info('') - - - # x - - - # display if opt_display: - # draw bbox - - # draw 3d landmarks - im_landmarks_3d_68 = im_resized.copy() - draw_utils.draw_landmarks3D(im_landmarks_3d_68, points_3d_68) - draw_utils.draw_bbox(im_landmarks_3d_68, bbox_dim) - - # draw 2d landmarks - im_landmarks_2d_68 = im_resized.copy() - draw_utils.draw_landmarks2D(im_landmarks_2d_68, points_2d_68) - draw_utils.draw_bbox(im_landmarks_2d_68, bbox_dim) - - # draw pose - if opt_run_pose: - im_pose = im_resized.copy() - draw_utils.draw_pose(im_pose, pose_data['point_nose'], pose_data['points']) - draw_utils.draw_degrees(im_pose, pose_data) + # draw landmarks + im_lmarks = im_resized.copy() + im_lmarks = draw_utils.draw_bbox(im_lmarks, bbox_norm) + im_lmarks = draw_utils.draw_landmarks2D(im_lmarks, points_2d_68, radius=1, color=(0,0,255)) + im_lmarks = draw_utils.draw_landmarks3D(im_lmarks, points_3d_68, radius=3, color=(0,255,0)) # draw animated GIF - im = Image.open(opt_fp_out) + im = Image.open(fp_out) im_frames = [] duration = im.info['duration'] + try: while True: im.seek(len(im_frames)) @@ -204,10 +155,7 @@ def cli(ctx, opt_fp_in, opt_fp_out, opt_gpu, opt_gif_frames, while True: # show all images here - cv.imshow('Original', im_resized) - cv.imshow('2D 68PT Landmarks', im_landmarks_2d_68) - cv.imshow('3D 68PT Landmarks', im_landmarks_3d_68) - cv.imshow('Pose', im_pose) + cv.imshow('2D/3D 68PT Landmarks', im_lmarks) cv.imshow('3D 68pt GIF', im_frames[frame_number]) frame_number = (frame_number + 1) % n_frames k = cv.waitKey(duration) & 0xFF diff --git a/megapixels/commands/demo/face_pose.py b/megapixels/commands/demo/face_pose.py index 3918adac..48214e0d 100644 --- a/megapixels/commands/demo/face_pose.py +++ b/megapixels/commands/demo/face_pose.py @@ -22,7 +22,7 @@ from app.settings import app_cfg as cfg help='GPU index') @click.option('-f', '--force', 'opt_force', is_flag=True, help='Force overwrite file') -@click.option('--display/--no-display', 'opt_display', is_flag=True, default=False, +@click.option('--display/--no-display', 'opt_display', is_flag=True, default=True, help='Display detections to debug') @click.pass_context def cli(ctx, opt_fp_in, opt_fp_out, opt_gpu, opt_size, opt_force, opt_display): @@ -61,12 +61,12 @@ def cli(ctx, opt_fp_in, opt_fp_out, opt_gpu, opt_size, opt_force, opt_display): # ---------------------------------------------------------------------------- # detect face - face_detector = face_detector.DetectorDLIBCNN(gpu=opt_gpu) # -1 for CPU + face_detector = face_detector.DetectorCVDNN() bboxes = face_detector.detect(im_resized, largest=True) - bbox = bboxes[0] + bbox_norm = bboxes[0] dim = im_resized.shape[:2][::-1] - bbox_dim = bbox.to_dim(dim) - if not bbox: + bbox_dim = bbox_norm.to_dim(dim) + if not bbox_norm: log.error('no face detected') return @@ -76,7 +76,7 @@ def cli(ctx, opt_fp_in, opt_fp_out, opt_gpu, opt_size, opt_force, opt_display): from app.processors import face_landmarks landmark_detector_2d_68 = face_landmarks.Dlib2D_68() - points_2d_68 = landmark_detector_2d_68.landmarks(im_resized, bbox_dim) + points_2d_68 = landmark_detector_2d_68.landmarks(im_resized, bbox_norm) # ---------------------------------------------------------------------------- @@ -97,14 +97,14 @@ def cli(ctx, opt_fp_in, opt_fp_out, opt_gpu, opt_size, opt_force, opt_display): # draw # draw 2d landmarks - im_landmarks_2d_68 = im_resized.copy() - draw_utils.draw_landmarks2D(im_landmarks_2d_68, points_2d_68) - draw_utils.draw_bbox(im_landmarks_2d_68, bbox_dim) + im_landmarks = im_resized.copy() + im_landmarks = draw_utils.draw_landmarks2D(im_landmarks, points_2d_68) + im_landmarks = draw_utils.draw_bbox(im_landmarks, bbox_norm) # draw pose im_pose = im_resized.copy() - draw_utils.draw_pose(im_pose, pose_data['point_nose'], pose_data['points']) - draw_utils.draw_degrees(im_pose, pose_data) + im_pose = draw_utils.draw_pose(im_pose, pose_data['point_nose'], pose_data['points']) + im_pose = draw_utils.draw_degrees(im_pose, pose_data) # ---------------------------------------------------------------------------- @@ -120,9 +120,8 @@ def cli(ctx, opt_fp_in, opt_fp_out, opt_gpu, opt_size, opt_force, opt_display): if opt_display: - # show all images here cv.imshow('Original', im_resized) - cv.imshow('2D 68PT Landmarks', im_landmarks_2d_68) + cv.imshow('2D 68PT Landmarks', im_landmarks) cv.imshow('Pose', im_pose) display_utils.handle_keyboard() \ No newline at end of file diff --git a/megapixels/commands/demo/face_search.py b/megapixels/commands/demo/face_search.py index ca0b8016..d50f5c73 100644 --- a/megapixels/commands/demo/face_search.py +++ b/megapixels/commands/demo/face_search.py @@ -13,7 +13,7 @@ log = Logger.getLogger() help='File to lookup') @click.option('--data_store', 'opt_data_store', type=cfg.DataStoreVar, - default=click_utils.get_default(types.DataStore.SSD), + default=click_utils.get_default(types.DataStore.HDD), show_default=True, help=click_utils.show_help(types.DataStore)) @click.option('--dataset', 'opt_dataset', @@ -40,44 +40,47 @@ def cli(ctx, opt_fp_in, opt_data_store, opt_dataset, opt_results, opt_gpu): from tqdm import tqdm import imutils - from app.utils import file_utils, im_utils + from app.utils import file_utils, im_utils, display_utils, draw_utils from app.models.data_store import DataStore from app.processors import face_detector - from app.processors import face_recognition + from app.processors import face_extractor log = Logger.getLogger() + # init dataset dataset = Dataset(opt_data_store, opt_dataset) dataset.load_face_vectors() dataset.load_records() - dataset.load_identities() + # dataset.load_identities() # init face detection - detector = face_detector.DetectorDLIBHOG() + detector = face_detector.DetectorCVDNN() - # init face recognition - recognition = face_recognition.RecognitionDLIB(gpu=opt_gpu) + # init face extractor + extractor = face_extractor.ExtractorVGG() # load query image im_query = cv.imread(opt_fp_in) # get detection as BBox object bboxes = detector.detect(im_query, largest=True) - bbox = bboxes[0] + bbox_norm = bboxes[0] dim = im_query.shape[:2][::-1] - bbox = bbox.to_dim(dim) # convert back to real dimensions + bbox_dim = bbox_norm.to_dim(dim) # convert back to real dimensions - if not bbox: + if not bbox_norm: log.error('No face detected. Exiting') return # extract the face vectors - vec_query = recognition.vec(im_query, bbox) + vec_query = extractor.extract(im_query, bbox_norm) + log.debug(f'len query: {len(vec_query)}') # find matches image_records = dataset.find_matches(vec_query, n_results=opt_results) # summary + im_query = draw_utils.draw_bbox(im_query, bbox_norm, stroke_weight=8) ims_match = [im_query] for image_record in image_records: image_record.summarize() @@ -85,16 +88,11 @@ def cli(ctx, opt_fp_in, opt_data_store, opt_dataset, opt_results, opt_gpu): im_match = cv.imread(image_record.filepath) ims_match.append(im_match) + # make montages of most similar faces montages = imutils.build_montages(ims_match, (256, 256), (3,2)) + # display for i, montage in enumerate(montages): - cv.imshow(f'{i}', montage) - # cv gui - 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 + cv.imshow(f'{opt_dataset.name.upper()}: page {i}', montage) + + display_utils.handle_keyboard() diff --git a/megapixels/commands/demo/face_vector.py b/megapixels/commands/demo/face_vector.py index 3ff68001..c7b5ef2e 100644 --- a/megapixels/commands/demo/face_vector.py +++ b/megapixels/commands/demo/face_vector.py @@ -1,9 +1,8 @@ """ -Crop images to prepare for training +Tests if the feature vector generator works """ import click -# from PIL import Image, ImageOps, ImageFilter, ImageDraw from app.settings import types from app.utils import click_utils @@ -14,11 +13,11 @@ from app.settings import app_cfg as cfg @click.option('-i', '--input', 'opt_fp_in', default=None, required=True, help='Image filepath') @click.option('--size', 'opt_size', - type=(int, int), default=(300, 300), + type=(int, int), default=cfg.DEFAULT_SIZE_FACE_DETECT, help='Output image size') @click.option('-g', '--gpu', 'opt_gpu', default=0, help='GPU index') -@click.option('--display/--no-display', 'opt_display', is_flag=True, default=False, +@click.option('--display/--no-display', 'opt_display', is_flag=True, default=True, help='Display detections to debug') @click.pass_context def cli(ctx, opt_fp_in, opt_gpu, opt_size, opt_display): @@ -54,12 +53,12 @@ def cli(ctx, opt_fp_in, opt_gpu, opt_size, opt_display): # ---------------------------------------------------------------------------- # detect face - face_detector = face_detector.DetectorDLIBCNN(gpu=opt_gpu) # -1 for CPU + face_detector = face_detector.DetectorCVDNN() # -1 for CPU bboxes = face_detector.detect(im_resized, largest=True) - bbox = bboxes[0] + bbox_norm = bboxes[0] dim = im_resized.shape[:2][::-1] - bbox_dim = bbox.to_dim(dim) - if not bbox: + bbox_dim = bbox_norm.to_dim(dim) + if not bbox_norm: log.error('no face detected') return @@ -67,14 +66,13 @@ def cli(ctx, opt_fp_in, opt_gpu, opt_size, opt_display): # ---------------------------------------------------------------------------- # generate face vectors, only to test if feature extraction works - from app.processors import face_recognition - facerec = face_recognition.RecognitionDLIB() - vec = facerec.vec(im_resized, bbox_dim) - vec_flat = facerec.flatten(vec) - log.info(f'generated vector. showing vec[0:10]:') - log.info(f'\n{vec_flat}') + from app.processors import face_extractor + extractor = face_extractor.ExtractorVGG() + vec = extractor.extract(im_resized, bbox_norm) + vec_str = extractor.to_str(vec) + log.info(f'\n{vec_str}') if opt_display: - draw_utils.draw_bbox(im_resized, bbox_dim) + im_resized = draw_utils.draw_bbox(im_resized, bbox_dim) cv.imshow('Original', im_resized) display_utils.handle_keyboard() \ No newline at end of file -- cgit v1.2.3-70-g09d2 From cb4d6d6f5be213edbc4f3b1e4452e5b7ce5e9378 Mon Sep 17 00:00:00 2001 From: adamhrv Date: Thu, 17 Jan 2019 11:26:41 +0100 Subject: updates for batch processing --- megapixels/app/models/data_store.py | 3 + megapixels/app/models/dataset.py | 67 ++++++--- megapixels/app/processors/face_detector.py | 30 ++++- megapixels/app/settings/app_cfg.py | 7 + megapixels/app/settings/types.py | 6 +- megapixels/app/utils/display_utils.py | 6 +- megapixels/commands/cv/face_attributes.py | 21 ++- megapixels/commands/cv/face_pose.py | 2 +- megapixels/commands/cv/face_roi.py | 61 +++++---- megapixels/commands/cv/face_vector.py | 3 +- megapixels/commands/cv/resize.py | 13 +- megapixels/commands/cv/resize_dataset.py | 149 +++++++++++++++++++++ megapixels/commands/datasets/file_record.py | 2 +- megapixels/commands/demo/face_search.py | 11 +- .../face_analysis/face_recognition_vgg.ipynb | 36 ++++- 15 files changed, 336 insertions(+), 81 deletions(-) create mode 100644 megapixels/commands/cv/resize_dataset.py (limited to 'megapixels/commands/demo') diff --git a/megapixels/app/models/data_store.py b/megapixels/app/models/data_store.py index 626c9da4..a8d6916f 100644 --- a/megapixels/app/models/data_store.py +++ b/megapixels/app/models/data_store.py @@ -24,6 +24,9 @@ class DataStore: def metadata_dir(self): return join(self.dir_metadata) + def media_dir(self): + return join(self.dir_media) + def media_images_original(self): return join(self.dir_media, 'original') diff --git a/megapixels/app/models/dataset.py b/megapixels/app/models/dataset.py index bbef9ff5..1b91467b 100644 --- a/megapixels/app/models/dataset.py +++ b/megapixels/app/models/dataset.py @@ -32,7 +32,7 @@ class Dataset: self.data_store = DataStore(opt_data_store, self._dataset_type) self.data_store_s3 = DataStoreS3(self._dataset_type) - def load_face_vectors(self): + def _load_face_vectors(self): metadata_type = types.Metadata.FACE_VECTOR fp_csv = self.data_store.metadata(metadata_type) self.log.info(f'loading: {fp_csv}') @@ -51,18 +51,17 @@ class Dataset: self.log.error(f'File not found: {fp_csv}. Exiting.') sys.exit() - def load_records(self): + def _load_file_records(self): metadata_type = types.Metadata.FILE_RECORD fp_csv = self.data_store.metadata(metadata_type) self.log.info(f'loading: {fp_csv}') if Path(fp_csv).is_file(): - self._metadata[metadata_type] = pd.read_csv(fp_csv, dtype={'fn':str}).set_index('index') + self._metadata[metadata_type] = pd.read_csv(fp_csv, dtype=cfg.FILE_RECORD_DTYPES).set_index('index') else: self.log.error(f'File not found: {fp_csv}. Exiting.') sys.exit() - def load_identities(self): - metadata_type = types.Metadata.IDENTITY + def _load_metadata(self, metadata_type): fp_csv = self.data_store.metadata(metadata_type) self.log.info(f'loading: {fp_csv}') if Path(fp_csv).is_file(): @@ -70,6 +69,14 @@ class Dataset: else: self.log.error(f'File not found: {fp_csv}. Exiting.') sys.exit() + + def load_metadata(self, metadata_type): + if metadata_type == types.Metadata.FILE_RECORD: + self._load_file_records() + elif metadata_type == types.Metadata.FACE_VECTOR: + self._load_face_vectors() + else: + self._load_metadata(metadata_type) def metadata(self, opt_metadata_type): return self._metadata.get(opt_metadata_type, None) @@ -82,11 +89,11 @@ class Dataset: # get identity meta df_identity = self._metadata[types.Metadata.IDENTITY] # future datasets can have multiple identities per images - ds_identities = df_identity.iloc[identity_index] + #ds_identities = df_identity.iloc[identity_index] # get filepath and S3 url fp_im = self.data_store.face(ds_record.subdir, ds_record.fn, ds_record.ext) s3_url = self.data_store_s3.face(ds_record.uuid) - image_record = ImageRecord(ds_record, fp_im, s3_url, ds_identities=ds_identities) + image_record = ImageRecord(ds_record, fp_im, s3_url) return image_record def vector_to_record(self, record_index): @@ -149,7 +156,14 @@ class Dataset: df_vector = self._metadata[types.Metadata.FACE_VECTOR] df_record = self._metadata[types.Metadata.FILE_RECORD] - + if types.Metadata.IDENTITY in self._metadata.keys(): + df_identity = self._metadata[types.Metadata.IDENTITY] + else: + df_identity = None + df_roi = self._metadata[types.Metadata.FACE_ROI] + + identities = [] + for match_idx in match_idxs: # get the corresponding face vector row roi_index = self._face_vector_roi_idxs[match_idx] @@ -158,7 +172,18 @@ class Dataset: self.log.debug(f'find match index: {match_idx}, --> roi_index: {roi_index}') fp_im = self.data_store.face(ds_record.subdir, ds_record.fn, ds_record.ext) s3_url = self.data_store_s3.face(ds_record.uuid) - image_record = ImageRecord(ds_record, fp_im, s3_url) + identities = [] + if types.Metadata.IDENTITY in self._metadata.keys(): + ds_id = df_identity.loc[df_identity['identity_key'] == ik].iloc[0] + identity = Identity(idx, + name_display=ds_id.name_display, + name_full=ds_id.name_full, + description=ds_id.description, + gender=ds_id.gender, + image_index=ds_id.image_index, + identity_key=ds_id.identity_key) + identities.append(identity) + image_record = ImageRecord(ds_record, fp_im, s3_url, identities=identities) image_records.append(image_record) return image_records @@ -191,19 +216,20 @@ class Dataset: class ImageRecord: - def __init__(self, ds_record, fp, url, ds_rois=None, ds_identities=None): + def __init__(self, ds_record, fp, url, rois=None, identities=None): # maybe more other meta will go there self.image_index = ds_record.index self.sha256 = ds_record.sha256 self.uuid = ds_record.uuid self.filepath = fp + self.width = ds_record.width + self.height = ds_record.height self.url = url - self._identities = [] + self.rois = rois + self.identities = identities # image records contain ROIs # ROIs are linked to identities - #self._identities = [Identity(x) for x in ds_identities] - @property def identity(self, index): return self._identity @@ -215,7 +241,7 @@ class ImageRecord: log.info(f'sha256: {self.sha256}') log.info(f'UUID: {self.uuid}') log.info(f'S3 url: {self.url}') - for identity in self._identities: + for identity in self.identities: log.info(f'fullname: {identity.fullname}') log.info(f'description: {identity.description}') log.info(f'gender: {identity.gender}') @@ -224,13 +250,10 @@ class ImageRecord: class Identity: - def __init__(self, idx, name='NA', desc='NA', gender='NA', n_images=1, - url='NA', age='NA', nationality='NA'): + def __init__(self, idx, name_display=None, name_full=None, description=None, gender=None, roi=None): self.index = idx - self.name = name - self.description = desc + self.name_display = name_display + self.name_full = name_full + self.description = description self.gender = gender - self.n_images = n_images - self.url = url - self.age = age - self.nationality = nationality + self.roi = roi diff --git a/megapixels/app/processors/face_detector.py b/megapixels/app/processors/face_detector.py index fbf91071..7b5310c5 100644 --- a/megapixels/app/processors/face_detector.py +++ b/megapixels/app/processors/face_detector.py @@ -69,6 +69,7 @@ class DetectorMTCNN_TF: # pip install mtcnn dnn_size = (300, 300) + conf_thresh = 0.9 def __init__(self, size=(400,400), gpu=0): self.log = logger_utils.Logger.getLogger() @@ -84,17 +85,33 @@ class DetectorMTCNN_TF: :param im: (numpy.ndarray) image :returns list of BBox ''' + bboxes = [] dnn_size = self.dnn_size if size is None else size + conf_thresh = self.conf_thresh if conf_thresh is None else conf_thresh im = im_utils.resize(im, width=dnn_size[0], height=dnn_size[1]) dim = im.shape[:2][::-1] dets = self.detector.detect_faces(im) + ''' + { + 'box': [4, 140, 14, 18], + 'confidence': 0.9588413834571838, + 'keypoints': { + 'left_eye': (8, 147), + 'right_eye': (14, 146), + 'nose': (12, 151), + 'mouth_left': (9, 155), + 'mouth_right': (14, 154) + } + } + ''' for det in dets: rect = det['box'] - #keypoints = det['keypoints'] # not using here. see 'face_landmarks.py' - bbox = BBox.from_xywh_dim(*rect, dim) - bboxes.append(bbox) + conf = det['confidence'] + if conf > conf_thresh: + bbox = BBox.from_xywh_dim(*rect, dim) + bboxes.append(bbox) if largest and len(bboxes) > 1: # only keep largest @@ -222,8 +239,11 @@ class DetectorCVDNN: bboxes = [] for i in range(0, net_outputs.shape[2]): - conf = net_outputs[0, 0, i, 2] - if conf > conf_thresh: + conf = float(net_outputs[0, 0, i, 2]) + # BUG: this face detector creates ghost face detections in stage-left from nose-bottom neck + # temp fix is to elminate ROI extending outside of frame + bounds = np.array(net_outputs[0, 0, i, 3:7]) + if conf > conf_thresh and np.all(bounds < 1): rect_norm = net_outputs[0, 0, i, 3:7] bboxes.append(BBox(*rect_norm)) diff --git a/megapixels/app/settings/app_cfg.py b/megapixels/app/settings/app_cfg.py index 42e37b7a..c256635b 100644 --- a/megapixels/app/settings/app_cfg.py +++ b/megapixels/app/settings/app_cfg.py @@ -110,6 +110,13 @@ POSE_MINMAX_PITCH = (-10,10) POSE_MINMAX_YAW = (-40,40) POSE_MINMAX_ROLL = (-35,35) POSE_MINMAX_PITCH = (-25,25) + +# ----------------------------------------------------------------------------- +# Pandas data +# ----------------------------------------------------------------------------- + +FILE_RECORD_DTYPES = {'fn':str, 'subdir': str} + # ----------------------------------------------------------------------------- # Logging options exposed for custom click Params # ----------------------------------------------------------------------------- diff --git a/megapixels/app/settings/types.py b/megapixels/app/settings/types.py index 9325fc3c..7a34ccc2 100644 --- a/megapixels/app/settings/types.py +++ b/megapixels/app/settings/types.py @@ -43,10 +43,12 @@ class LogLevel(Enum): class Metadata(Enum): IDENTITY, FILE_RECORD, FACE_VECTOR, FACE_POSE, \ - FACE_ROI, FACE_LANDMARK_2D_68, FACE_LANDMARK_2D_5,FACE_LANDMARK_3D_68, FACE_ATTRIBUTES = range(9) + FACE_ROI, FACE_LANDMARK_2D_68, FACE_LANDMARK_2D_5,FACE_LANDMARK_3D_68, \ + FACE_ATTRIBUTES = range(9) class Dataset(Enum): - LFW, VGG_FACE2, MSCELEB, UCCS, UMD_FACES, SCUT_FBP, UCF_SELFIE, UTK = range(8) + LFW, VGG_FACE2, MSCELEB, UCCS, UMD_FACES, SCUT_FBP, UCF_SELFIE, UTK, \ + CASIA_WEBFACE, AFW, PUBFIG83, HELEN, PIPA, MEGAFACE = range(14) # --------------------------------------------------------------------- diff --git a/megapixels/app/utils/display_utils.py b/megapixels/app/utils/display_utils.py index e72cc0f0..43328ae9 100644 --- a/megapixels/app/utils/display_utils.py +++ b/megapixels/app/utils/display_utils.py @@ -15,5 +15,7 @@ def handle_keyboard(delay_amt=1): if k == 27 or k == ord('q'): # ESC cv.destroyAllWindows() sys.exit() - #else: - #log.info('Press Q, q, or ESC to exit') + elif k == 32 or k == 83: # 83 = right arrow + break + elif k != 255: + log.debug(f'k: {k}') diff --git a/megapixels/commands/cv/face_attributes.py b/megapixels/commands/cv/face_attributes.py index bb7978f7..01fe3bd1 100644 --- a/megapixels/commands/cv/face_attributes.py +++ b/megapixels/commands/cv/face_attributes.py @@ -77,7 +77,7 @@ def cli(ctx, opt_fp_in, opt_fp_out, opt_dir_media, opt_data_store, opt_dataset, # ------------------------------------------------------------------------- # load filepath data fp_record = data_store.metadata(types.Metadata.FILE_RECORD) - df_record = pd.read_csv(fp_record, dtype={'fn':str}).set_index('index') + df_record = pd.read_csv(fp_record, dtype=cfg.FILE_RECORD_DTYPES).set_index('index') # load ROI data fp_roi = data_store.metadata(types.Metadata.FACE_ROI) df_roi = pd.read_csv(fp_roi).set_index('index') @@ -112,18 +112,15 @@ def cli(ctx, opt_fp_in, opt_fp_out, opt_dir_media, opt_data_store, opt_dataset, bbox_norm = BBox.from_xywh(df_img.x, df_img.y, df_img.w, df_img.h) bbox_dim = bbox_norm.to_dim(dim) - #age_apnt = age_estimator_apnt.predict(im_resized, bbox_norm) - #age_real = age_estimator_real.predict(im_resized, bbox_norm) - #gender = gender_estimator.predict(im_resized, bbox_norm) - - # attr_obj = { - # 'age_real':float(f'{age_real:.2f}'), - # 'age_apparent': float(f'{age_apnt:.2f}'), - # 'm': float(f'{gender["m"]:.4f}'), - # 'f': float(f'{gender["f"]:.4f}'), - # 'roi_index': roi_index - # } + age_apnt = age_estimator_apnt.predict(im_resized, bbox_norm) + age_real = age_estimator_real.predict(im_resized, bbox_norm) + gender = gender_estimator.predict(im_resized, bbox_norm) + attr_obj = { + 'age_real':float(f'{age_real:.2f}'), + 'age_apparent': float(f'{age_apnt:.2f}'), + 'm': float(f'{gender["m"]:.4f}'), + 'f': float(f'{gender["f"]:.4f}'), 'roi_index': roi_index } results.append(attr_obj) diff --git a/megapixels/commands/cv/face_pose.py b/megapixels/commands/cv/face_pose.py index 75db603b..cb7ec56c 100644 --- a/megapixels/commands/cv/face_pose.py +++ b/megapixels/commands/cv/face_pose.py @@ -92,7 +92,7 @@ def cli(ctx, opt_fp_in, opt_fp_out, opt_dir_media, opt_data_store, opt_dataset, # load data fp_record = data_store.metadata(types.Metadata.FILE_RECORD) - df_record = pd.read_csv(fp_record, dtype={'fn':str}).set_index('index') + df_record = pd.read_csv(fp_record, dtype=cfg.FILE_RECORD_DTYPES).set_index('index') # load ROI data fp_roi = data_store.metadata(types.Metadata.FACE_ROI) df_roi = pd.read_csv(fp_roi).set_index('index') diff --git a/megapixels/commands/cv/face_roi.py b/megapixels/commands/cv/face_roi.py index 950936cf..e83b0f61 100644 --- a/megapixels/commands/cv/face_roi.py +++ b/megapixels/commands/cv/face_roi.py @@ -105,23 +105,29 @@ def cli(ctx, opt_fp_in, opt_dir_media, opt_fp_out, opt_data_store, opt_dataset, # get list of files to process - fp_in = data_store.metadata(types.Metadata.FILE_RECORD) if opt_fp_in is None else opt_fp_in - df_records = pd.read_csv(fp_in, dtype={'fn':str}).set_index('index') + fp_record = data_store.metadata(types.Metadata.FILE_RECORD) if opt_fp_in is None else opt_fp_in + df_record = pd.read_csv(fp_record, dtype=cfg.FILE_RECORD_DTYPES).set_index('index') if opt_slice: - df_records = df_records[opt_slice[0]:opt_slice[1]] - log.debug('processing {:,} files'.format(len(df_records))) + df_record = df_record[opt_slice[0]:opt_slice[1]] + log.debug('processing {:,} files'.format(len(df_record))) # filter out grayscale color_filter = color_filters[opt_color_filter] # set largest flag, to keep all or only largest - opt_largest = opt_largest == 'largest' + opt_largest = (opt_largest == 'largest') data = [] + skipped_files = [] + processed_files = [] - for df_record in tqdm(df_records.itertuples(), total=len(df_records)): + for df_record in tqdm(df_record.itertuples(), total=len(df_record)): fp_im = data_store.face(str(df_record.subdir), str(df_record.fn), str(df_record.ext)) - im = cv.imread(fp_im) - im_resized = im_utils.resize(im, width=opt_size[0], height=opt_size[1]) + try: + im = cv.imread(fp_im) + im_resized = im_utils.resize(im, width=opt_size[0], height=opt_size[1]) + except Exception as e: + log.debug(f'could not read: {fp_im}') + return # filter out color or grayscale iamges if color_filter != color_filters['all']: try: @@ -134,31 +140,38 @@ def cli(ctx, opt_fp_in, opt_dir_media, opt_fp_out, opt_data_store, opt_dataset, continue try: - bboxes = detector.detect(im_resized, pyramids=opt_pyramids, largest=opt_largest, + bboxes_norm = detector.detect(im_resized, pyramids=opt_pyramids, largest=opt_largest, zone=opt_zone, conf_thresh=opt_conf_thresh) except Exception as e: log.error('could not detect: {}'.format(fp_im)) log.error('{}'.format(e)) continue - for bbox in bboxes: - roi = { - 'record_index': int(df_record.Index), - 'x': bbox.x, - 'y': bbox.y, - 'w': bbox.w, - 'h': bbox.h - } - data.append(roi) - if len(bboxes) == 0: + if len(bboxes_norm) == 0: + skipped_files.append(fp_im) log.warn(f'no faces in: {fp_im}') - + log.warn(f'skipped: {len(skipped_files)}. found:{len(processed_files)} files') + else: + processed_files.append(fp_im) + for bbox in bboxes_norm: + roi = { + 'record_index': int(df_record.Index), + 'x': bbox.x, + 'y': bbox.y, + 'w': bbox.w, + 'h': bbox.h + } + data.append(roi) + # if display optined - if opt_display and len(bboxes): + if opt_display and len(bboxes_norm): # draw each box - for bbox in bboxes: - bbox_dim = bbox.to_dim(im_resized.shape[:2][::-1]) - draw_utils.draw_bbox(im_resized, bbox_dim) + for bbox_norm in bboxes_norm: + dim = im_resized.shape[:2][::-1] + bbox_dim = bbox.to_dim(dim) + if dim[0] > 1000: + im_resized = im_utils.resize(im_resized, width=1000) + im_resized = draw_utils.draw_bbox(im_resized, bbox_norm) # display and wait cv.imshow('', im_resized) diff --git a/megapixels/commands/cv/face_vector.py b/megapixels/commands/cv/face_vector.py index 9a527bc3..cb155d08 100644 --- a/megapixels/commands/cv/face_vector.py +++ b/megapixels/commands/cv/face_vector.py @@ -88,7 +88,7 @@ def cli(ctx, opt_fp_out, opt_dir_media, opt_data_store, opt_dataset, opt_size, # load data fp_record = data_store.metadata(types.Metadata.FILE_RECORD) - df_record = pd.read_csv(fp_record, dtype={'fn':str}).set_index('index') + df_record = pd.read_csv(fp_record, dtype=cfg.FILE_RECORD_DTYPES).set_index('index') fp_roi = data_store.metadata(types.Metadata.FACE_ROI) df_roi = pd.read_csv(fp_roi).set_index('index') @@ -107,6 +107,7 @@ def cli(ctx, opt_fp_out, opt_dir_media, opt_data_store, opt_dataset, opt_size, 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) + im = im_utils.resize(im, width=opt_size[0], height=opt_size[1]) 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 diff --git a/megapixels/commands/cv/resize.py b/megapixels/commands/cv/resize.py index dcd621b3..7409ee6f 100644 --- a/megapixels/commands/cv/resize.py +++ b/megapixels/commands/cv/resize.py @@ -49,7 +49,7 @@ centerings = { help='File glob ext') @click.option('--size', 'opt_size', type=(int, int), default=(256, 256), - help='Output image size (square)') + help='Max output size') @click.option('--method', 'opt_scale_method', type=click.Choice(methods.keys()), default='lanczos', @@ -88,7 +88,7 @@ def cli(ctx, opt_dir_in, opt_dir_out, opt_glob_ext, opt_size, opt_scale_method, # ------------------------------------------------- # process here - def pool_resize(fp_im, opt_size, scale_method, centering): + def pool_resize(fp_im, opt_size, scale_method): # Threaded image resize function try: pbar.update(1) @@ -100,7 +100,7 @@ def cli(ctx, opt_dir_in, opt_dir_out, opt_glob_ext, opt_size, opt_scale_method, log.error(e) return False - im = ImageOps.fit(im, opt_size, method=scale_method, centering=centering) + #im = ImageOps.fit(im, opt_size, method=scale_method, centering=centering) if opt_equalize: im_np = im_utils.pil2np(im) @@ -117,8 +117,8 @@ def cli(ctx, opt_dir_in, opt_dir_out, opt_glob_ext, opt_size, opt_scale_method, except: return False - centering = centerings[opt_center] - scale_method = methods[opt_scale_method] + #centering = centerings[opt_center] + #scale_method = methods[opt_scale_method] # get list of files to process fp_ims = glob(join(opt_dir_in, '*.{}'.format(opt_glob_ext))) @@ -132,7 +132,8 @@ def cli(ctx, opt_dir_in, opt_dir_out, opt_glob_ext, opt_size, opt_scale_method, # setup multithreading pbar = tqdm(total=len(fp_ims)) - pool_resize = partial(pool_resize, opt_size=opt_size, scale_method=scale_method, centering=centering) + #pool_resize = partial(pool_resize, opt_size=opt_size, scale_method=scale_method, centering=centering) + pool_resize = partial(pool_resize, opt_size=opt_size) #result_list = pool.map(prod_x, data_list) pool = ThreadPool(opt_threads) with tqdm(total=len(fp_ims)) as pbar: diff --git a/megapixels/commands/cv/resize_dataset.py b/megapixels/commands/cv/resize_dataset.py new file mode 100644 index 00000000..3a6ec15f --- /dev/null +++ b/megapixels/commands/cv/resize_dataset.py @@ -0,0 +1,149 @@ +""" +Crop images to prepare for training +""" + +import click +import cv2 as cv +from PIL import Image, ImageOps, ImageFilter + +from app.settings import types +from app.utils import click_utils +from app.settings import app_cfg as cfg + +cv_resize_algos = { + 'area': cv.INTER_AREA, + 'lanco': cv.INTER_LANCZOS4, + 'linear': cv.INTER_LINEAR, + 'linear_exact': cv.INTER_LINEAR_EXACT, + 'nearest': cv.INTER_NEAREST +} +""" +Filter Q-Down Q-Up Speed +NEAREST ⭐⭐⭐⭐⭐ +BOX ⭐ ⭐⭐⭐⭐ +BILINEAR ⭐ ⭐ ⭐⭐⭐ +HAMMING ⭐⭐ ⭐⭐⭐ +BICUBIC ⭐⭐⭐ ⭐⭐⭐ ⭐⭐ +LANCZOS ⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐ +""" +pil_resize_algos = { + 'antialias': Image.ANTIALIAS, + 'lanczos': Image.LANCZOS, + 'bicubic': Image.BICUBIC, + 'hamming': Image.HAMMING, + 'bileaner': Image.BILINEAR, + 'box': Image.BOX, + 'nearest': Image.NEAREST + } + +@click.command() +@click.option('--dataset', 'opt_dataset', + type=cfg.DatasetVar, + required=True, + show_default=True, + help=click_utils.show_help(types.Dataset)) +@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('-o', '--output', 'opt_dir_out', required=True, + help='Output directory') +@click.option('-e', '--ext', 'opt_glob_ext', + default='png', type=click.Choice(['jpg', 'png']), + help='File glob ext') +@click.option('--size', 'opt_size', + type=(int, int), default=(256, 256), + help='Output image size max (w,h)') +@click.option('--interp', 'opt_interp_algo', + type=click.Choice(pil_resize_algos.keys()), + default='bicubic', + help='Interpolation resizing algorithms') +@click.option('--slice', 'opt_slice', type=(int, int), default=(None, None), + help='Slice the input list') +@click.option('-t', '--threads', 'opt_threads', default=8, + help='Number of threads') +@click.option('--recursive/--no-recursive', 'opt_recursive', is_flag=True, default=False, + help='Use glob recursion (slower)') +@click.pass_context +def cli(ctx, opt_dataset, opt_data_store, opt_dir_out, opt_glob_ext, opt_size, opt_interp_algo, + opt_slice, opt_threads, opt_recursive): + """Resize dataset images""" + + import os + from os.path import join + from pathlib import Path + from glob import glob + from tqdm import tqdm + from multiprocessing.dummy import Pool as ThreadPool + from functools import partial + import pandas as pd + import numpy as np + + from app.utils import logger_utils, file_utils, im_utils + from app.models.data_store import DataStore + + # ------------------------------------------------- + # init + + log = logger_utils.Logger.getLogger() + + + # ------------------------------------------------- + # process here + + def pool_resize(fp_in, dir_in, dir_out, im_size, interp_algo): + # Threaded image resize function + pbar.update(1) + try: + im = Image.open(fp_in).convert('RGB') + im.verify() # throws error if image is corrupt + im.thumbnail(im_size, interp_algo) + fp_out = fp_in.replace(dir_in, dir_out) + file_utils.mkdirs(fp_out) + im.save(fp_out, quality=100) + except Exception as e: + log.warn(f'Could not open: {fp_in}, Error: {e}') + return False + return True + + + data_store = DataStore(opt_data_store, opt_dataset) + fp_records = data_store.metadata(types.Metadata.FILE_RECORD) + df_records = pd.read_csv(fp_records, dtype=cfg.FILE_RECORD_DTYPES).set_index('index') + dir_in = data_store.media_images_original() + + # get list of files to process + #fp_ims = file_utils.glob_multi(opt_dir_in, ['jpg', 'png'], recursive=opt_recursive) + fp_ims = [] + for ds_record in df_records.itertuples(): + fp_im = data_store.face(ds_record.subdir, ds_record.fn, ds_record.ext) + fp_ims.append(fp_im) + + if opt_slice: + fp_ims = fp_ims[opt_slice[0]:opt_slice[1]] + if not fp_ims: + log.error('No images. Try with "--recursive"') + return + log.info(f'processing {len(fp_ims):,} images') + + # algorithm to use for resizing + interp_algo = pil_resize_algos[opt_interp_algo] + log.info(f'using {interp_algo} for interpoloation') + + # ensure output dir exists + file_utils.mkdirs(opt_dir_out) + + # setup multithreading + pbar = tqdm(total=len(fp_ims)) + # fixed arguments for pool function + map_pool_resize = partial(pool_resize, dir_in=dir_in, dir_out=opt_dir_out, im_size=opt_size, interp_algo=interp_algo) + #result_list = pool.map(prod_x, data_list) # simple + pool = ThreadPool(opt_threads) + # start multithreading + with tqdm(total=len(fp_ims)) as pbar: + results = pool.map(map_pool_resize, fp_ims) + # end multithreading + pbar.close() + + log.info(f'Resized: {results.count(True)} / {len(fp_ims)} images') \ No newline at end of file diff --git a/megapixels/commands/datasets/file_record.py b/megapixels/commands/datasets/file_record.py index b5daef4e..41a5df28 100644 --- a/megapixels/commands/datasets/file_record.py +++ b/megapixels/commands/datasets/file_record.py @@ -127,7 +127,7 @@ def cli(ctx, opt_fp_in, opt_fp_out, opt_dataset, opt_data_store, opt_dir_media, sha256 = file_utils.sha256(fp_im) im = Image.open(fp_im) im.verify() # throws error if bad file - assert(im.size[0] > 100 and im.size[1] > 100) + assert(im.size[0] > 60 and im.size[1] > 60) except Exception as e: log.warn(f'skipping file: {fp_im}') return None diff --git a/megapixels/commands/demo/face_search.py b/megapixels/commands/demo/face_search.py index d50f5c73..f551cafd 100644 --- a/megapixels/commands/demo/face_search.py +++ b/megapixels/commands/demo/face_search.py @@ -39,6 +39,7 @@ def cli(ctx, opt_fp_in, opt_data_store, opt_dataset, opt_results, opt_gpu): import cv2 as cv from tqdm import tqdm import imutils + from PIL import Image, ImageOps from app.utils import file_utils, im_utils, display_utils, draw_utils from app.models.data_store import DataStore @@ -49,9 +50,10 @@ def cli(ctx, opt_fp_in, opt_data_store, opt_dataset, opt_results, opt_gpu): # init dataset dataset = Dataset(opt_data_store, opt_dataset) - dataset.load_face_vectors() - dataset.load_records() - # dataset.load_identities() + dataset.load_metadata(types.Metadata.FILE_RECORD) + dataset.load_metadata(types.Metadata.FACE_VECTOR) + dataset.load_metadata(types.Metadata.FACE_ROI) + # dataset.load_metadata(types.Metadata.IDENTITY) # init face detection detector = face_detector.DetectorCVDNN() @@ -86,6 +88,9 @@ def cli(ctx, opt_fp_in, opt_data_store, opt_dataset, opt_results, opt_gpu): image_record.summarize() log.info(f'{image_record.filepath}') im_match = cv.imread(image_record.filepath) + + im_match_pil = Image.open(image_record.filepath).convert('RGB') + # bbox = ims_match.append(im_match) # make montages of most similar faces diff --git a/megapixels/notebooks/face_analysis/face_recognition_vgg.ipynb b/megapixels/notebooks/face_analysis/face_recognition_vgg.ipynb index 45e167b4..e9808232 100644 --- a/megapixels/notebooks/face_analysis/face_recognition_vgg.ipynb +++ b/megapixels/notebooks/face_analysis/face_recognition_vgg.ipynb @@ -372,10 +372,42 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 452, "metadata": {}, "outputs": [], - "source": [] + "source": [ + "a = [.1, .2, .3, 1.1]" + ] + }, + { + "cell_type": "code", + "execution_count": 456, + "metadata": {}, + "outputs": [], + "source": [ + "from PIL import Image" + ] + }, + { + "cell_type": "code", + "execution_count": 464, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(590, 760) (372, 480)\n" + ] + } + ], + "source": [ + "fp_im_test = '/home/adam/Downloads/faces/snowden.jpg'\n", + "im_rs = Image.open(fp_im_test).convert('RGB')\n", + "im_rs_sm = im_rs.copy()\n", + "im_rs_sm.thumbnail((480,480))\n", + "print(im_rs.size, '', im_rs_sm.size)" + ] }, { "cell_type": "code", -- cgit v1.2.3-70-g09d2