From f215db6e84071077082d14f8366ae1cf1aea500f Mon Sep 17 00:00:00 2001 From: adamhrv Date: Thu, 3 Jan 2019 12:51:31 +0100 Subject: fix roi index, clean up pose, roi, records, vector --- megapixels/app/processors/face_detector.py | 8 ++++-- megapixels/app/processors/face_pose.py | 42 ++++++++++++++++++------------ 2 files changed, 31 insertions(+), 19 deletions(-) (limited to 'megapixels/app/processors') diff --git a/megapixels/app/processors/face_detector.py b/megapixels/app/processors/face_detector.py index 3a90c557..75ba54d4 100644 --- a/megapixels/app/processors/face_detector.py +++ b/megapixels/app/processors/face_detector.py @@ -24,7 +24,7 @@ class DetectorMTCNN: from mtcnn.mtcnn import MTCNN self.detector = MTCNN() - def detect(self, im, size=(400,400), conf_thresh=None, pyramids=None, largest=False): + def detect(self, im, size=(400,400), conf_thresh=None, pyramids=None, largest=False, zone=None): '''Detects face using MTCNN and returns (list) of BBox :param im: (numpy.ndarray) image :returns list of BBox @@ -79,7 +79,7 @@ class DetectorDLIBCNN: self.detector = dlib.cnn_face_detection_model_v1(cfg.DIR_MODELS_DLIB_CNN) os.environ['CUDA_VISIBLE_DEVICES'] = cuda_visible_devices # reset - def detect(self, im, size=None, conf_thresh=None, pyramids=None, largest=False): + def detect(self, im, size=None, conf_thresh=None, pyramids=None, largest=False, zone=None): bboxes = [] conf_thresh = self.conf_thresh if conf_thresh is None else conf_thresh pyramids = self.pyramids if pyramids is None else pyramids @@ -96,6 +96,10 @@ class DetectorDLIBCNN: bbox = BBox.from_dlib_dim(mmod_rect.rect, dim) bboxes.append(bbox) + if zone: + bboxes = [b for b in bboxes if b.cx > zone[0] and b.cx < 1.0 - zone[0] \ + and b.cy > zone[1] and b.cy < 1.0 - zone[1]] + if largest and len(bboxes) > 1: # only keep largest bboxes.sort(key=operator.attrgetter('area'), reverse=True) diff --git a/megapixels/app/processors/face_pose.py b/megapixels/app/processors/face_pose.py index f2548b32..96281637 100644 --- a/megapixels/app/processors/face_pose.py +++ b/megapixels/app/processors/face_pose.py @@ -18,12 +18,13 @@ class FacePoseDLIB: dnn_size = (400, 400) + pose_types = {'pitch': (0,0,255), 'roll': (255,0,0), 'yaw': (0,255,0)} def __init__(self): pass - def pose(self, landmarks, dim, project_points=False): + def pose(self, landmarks, dim): # computes pose using 6 / 68 points from dlib face landmarks # based on learnopencv.com and # https://github.com/jerryhouuu/Face-Yaw-Roll-Pitch-from-Pose-Estimation-using-OpenCV/ @@ -68,12 +69,17 @@ class FacePoseDLIB: result = {} # project points - if project_points: - pts_im, jac = cv.projectPoints(axis, rot_vec, tran_vec, cam_mat, dist_coeffs) - pts_model, jac2 = cv.projectPoints(model_points, rot_vec, tran_vec, cam_mat, dist_coeffs) - result['points_model'] = pts_model - result['points_image'] = pts_im - result['point_nose'] = tuple(landmarks[pose_points_idx[0]]) + #if project_points: + pts_im, jac = cv.projectPoints(axis, rot_vec, tran_vec, cam_mat, dist_coeffs) + pts_model, jac2 = cv.projectPoints(model_points, rot_vec, tran_vec, cam_mat, dist_coeffs) + #result['points_model'] = pts_model + #result['points_image'] = pts_im + result['points'] = { + 'pitch': pts_im[0], + 'roll': pts_im[2], + 'yaw': pts_im[1] + } + result['point_nose'] = tuple(landmarks[pose_points_idx[0]]) rvec_matrix = cv.Rodrigues(rot_vec)[0] @@ -84,21 +90,23 @@ class FacePoseDLIB: pitch = math.degrees(math.asin(math.sin(pitch))) roll = -math.degrees(math.asin(math.sin(roll))) yaw = math.degrees(math.asin(math.sin(yaw))) - degrees = {'pitch': pitch, 'roll': roll, 'yaw': yaw} - result['degrees'] = degrees + result['pitch'] = pitch + result['roll'] = roll + result['yaw'] = yaw return result - def draw_pose(self, im, pts_im, pts_model, pt_nose): - cv.line(im, pt_nose, tuple(pts_im[1].ravel()), (0,255,0), 3) #GREEN - cv.line(im, pt_nose, tuple(pts_im[0].ravel()), (255,0,), 3) #BLUE - cv.line(im, pt_nose, tuple(pts_im[2].ravel()), (0,0,255), 3) #RED + def draw_pose(self, im, pt_nose, image_pts): + cv.line(im, pt_nose, tuple(image_pts['pitch'].ravel()), self.pose_types['pitch'], 3) + cv.line(im, pt_nose, tuple(image_pts['yaw'].ravel()), self.pose_types['yaw'], 3) + cv.line(im, pt_nose, tuple(image_pts['roll'].ravel()), self.pose_types['roll'], 3) - def draw_degrees(self, im, degrees, color=(0,255,0)): - for i, item in enumerate(degrees.items()): - k, v = item + def draw_degrees(self, im, pose_data, color=(0,255,0)): + for i, pose_type in enumerate(self.pose_types.items()): + k, clr = pose_type + v = pose_data[k] t = '{}: {:.2f}'.format(k, v) origin = (10, 30 + (25 * i)) - cv.putText(im, t, origin, cv.FONT_HERSHEY_SIMPLEX, 0.5, color, thickness=2, lineType=2) \ No newline at end of file + cv.putText(im, t, origin, cv.FONT_HERSHEY_SIMPLEX, 0.5, clr, thickness=2, lineType=2) \ No newline at end of file -- cgit v1.2.3-70-g09d2 From b7aba5109bfdab302b82fe9021f16f73edbeb11d Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Thu, 3 Jan 2019 19:58:03 +0100 Subject: fix face detectors --- megapixels/app/processors/face_detector.py | 15 +++++-- megapixels/app/processors/face_landmarks_3d.py | 56 ++++++++++++++++++++++++-- megapixels/commands/cv/face_roi.py | 4 +- 3 files changed, 67 insertions(+), 8 deletions(-) (limited to 'megapixels/app/processors') diff --git a/megapixels/app/processors/face_detector.py b/megapixels/app/processors/face_detector.py index 75ba54d4..a805a474 100644 --- a/megapixels/app/processors/face_detector.py +++ b/megapixels/app/processors/face_detector.py @@ -119,7 +119,7 @@ class DetectorDLIBHOG: self.log = logger_utils.Logger.getLogger() self.detector = dlib.get_frontal_face_detector() - def detect(self, im, size=None, conf_thresh=None, pyramids=0, largest=False): + def detect(self, im, size=None, conf_thresh=None, pyramids=0, largest=False, zone=False): conf_thresh = self.conf_thresh if conf_thresh is None else conf_thresh dnn_size = self.size if size is None else size pyramids = self.pyramids if pyramids is None else pyramids @@ -136,8 +136,13 @@ class DetectorDLIBHOG: bbox = BBox.from_dlib_dim(rect, dim) bboxes.append(bbox) + # filter to keep on faces inside zone + if zone: + bboxes = [b for b in bboxes if b.cx > zone[0] and b.cx < 1.0 - zone[0] \ + and b.cy > zone[1] and b.cy < 1.0 - zone[1]] + + # filter to keep only largest face if largest and len(bboxes) > 1: - # only keep largest bboxes.sort(key=operator.attrgetter('area'), reverse=True) bboxes = [bboxes[0]] @@ -159,7 +164,7 @@ class DetectorCVDNN: self.net.setPreferableBackend(cv.dnn.DNN_BACKEND_OPENCV) self.net.setPreferableTarget(cv.dnn.DNN_TARGET_CPU) - def detect(self, im, size=None, conf_thresh=None, largest=False, pyramids=None): + def detect(self, im, size=None, conf_thresh=None, largest=False, pyramids=None, zone=False): """Detects faces and returns (list) of (BBox)""" conf_thresh = self.conf_thresh if conf_thresh is None else conf_thresh dnn_size = self.size if size is None else size @@ -175,6 +180,10 @@ class DetectorCVDNN: rect_norm = net_outputs[0, 0, i, 3:7] bboxes.append(BBox(*rect_norm)) + if zone: + bboxes = [b for b in bboxes if b.cx > zone[0] and b.cx < 1.0 - zone[0] \ + and b.cy > zone[1] and b.cy < 1.0 - zone[1]] + if largest and len(bboxes) > 1: # only keep largest bboxes.sort(key=operator.attrgetter('area'), reverse=True) diff --git a/megapixels/app/processors/face_landmarks_3d.py b/megapixels/app/processors/face_landmarks_3d.py index 84a423b0..28aff592 100644 --- a/megapixels/app/processors/face_landmarks_3d.py +++ b/megapixels/app/processors/face_landmarks_3d.py @@ -17,11 +17,61 @@ from app.settings import types class FaceLandmarks3D: # Estimates 3D facial landmarks + import face_alignment + from skimage import io def __init__(self): self.log = logger_utils.Logger.getLogger() - pass + self.fa = face_alignment.FaceAlignment(face_alignment.LandmarksType._2D, flip_input=False) + def landmarks(self, im): + preds = self.fa.get_landmarks(im) + return preds - def landmarks(self): - return [1,2,3,4,100] \ No newline at end of file + def draw(self, im): + '''draws landmarks in 3d scene''' + + ''' + import face_alignment + import numpy as np + from mpl_toolkits.mplot3d import Axes3D + import matplotlib.pyplot as plt + from skimage import io + + # Run the 3D face alignment on a test image, without CUDA. + fa = face_alignment.FaceAlignment(face_alignment.LandmarksType._3D, device='cuda:0', flip_input=True) + + input = io.imread('../test/assets/aflw-test.jpg') + preds = fa.get_landmarks(input)[-1] + + #TODO: Make this nice + fig = plt.figure(figsize=plt.figaspect(.5)) + ax = fig.add_subplot(1, 2, 1) + ax.imshow(input) + ax.plot(preds[0:17,0],preds[0:17,1],marker='o',markersize=6,linestyle='-',color='w',lw=2) + ax.plot(preds[17:22,0],preds[17:22,1],marker='o',markersize=6,linestyle='-',color='w',lw=2) + ax.plot(preds[22:27,0],preds[22:27,1],marker='o',markersize=6,linestyle='-',color='w',lw=2) + ax.plot(preds[27:31,0],preds[27:31,1],marker='o',markersize=6,linestyle='-',color='w',lw=2) + ax.plot(preds[31:36,0],preds[31:36,1],marker='o',markersize=6,linestyle='-',color='w',lw=2) + ax.plot(preds[36:42,0],preds[36:42,1],marker='o',markersize=6,linestyle='-',color='w',lw=2) + ax.plot(preds[42:48,0],preds[42:48,1],marker='o',markersize=6,linestyle='-',color='w',lw=2) + ax.plot(preds[48:60,0],preds[48:60,1],marker='o',markersize=6,linestyle='-',color='w',lw=2) + ax.plot(preds[60:68,0],preds[60:68,1],marker='o',markersize=6,linestyle='-',color='w',lw=2) + ax.axis('off') + + ax = fig.add_subplot(1, 2, 2, projection='3d') + surf = ax.scatter(preds[:,0]*1.2,preds[:,1],preds[:,2],c="cyan", alpha=1.0, edgecolor='b') + ax.plot3D(preds[:17,0]*1.2,preds[:17,1], preds[:17,2], color='blue' ) + ax.plot3D(preds[17:22,0]*1.2,preds[17:22,1],preds[17:22,2], color='blue') + ax.plot3D(preds[22:27,0]*1.2,preds[22:27,1],preds[22:27,2], color='blue') + ax.plot3D(preds[27:31,0]*1.2,preds[27:31,1],preds[27:31,2], color='blue') + ax.plot3D(preds[31:36,0]*1.2,preds[31:36,1],preds[31:36,2], color='blue') + ax.plot3D(preds[36:42,0]*1.2,preds[36:42,1],preds[36:42,2], color='blue') + ax.plot3D(preds[42:48,0]*1.2,preds[42:48,1],preds[42:48,2], color='blue') + ax.plot3D(preds[48:,0]*1.2,preds[48:,1],preds[48:,2], color='blue' ) + + ax.view_init(elev=90., azim=90.) + ax.set_xlim(ax.get_xlim()[::-1]) + plt.show() + ''' + return False \ No newline at end of file diff --git a/megapixels/commands/cv/face_roi.py b/megapixels/commands/cv/face_roi.py index a09a1ce0..c3c2ac05 100644 --- a/megapixels/commands/cv/face_roi.py +++ b/megapixels/commands/cv/face_roi.py @@ -18,7 +18,7 @@ color_filters = {'color': 1, 'gray': 2, 'all': 3} help='Override enum output filename CSV') @click.option('-m', '--media', 'opt_dir_media', default=None, help='Override enum media directory') -@click.option('--data_store', 'opt_data_store', +@click.option('--store', 'opt_data_store', type=cfg.DataStoreVar, default=click_utils.get_default(types.DataStore.HDD), show_default=True, @@ -31,7 +31,7 @@ color_filters = {'color': 1, 'gray': 2, 'all': 3} @click.option('--size', 'opt_size', type=(int, int), default=(300, 300), help='Output image size') -@click.option('-t', '--detector-type', 'opt_detector_type', +@click.option('-d', '--detector', 'opt_detector_type', type=cfg.FaceDetectNetVar, default=click_utils.get_default(types.FaceDetectNet.DLIB_CNN), help=click_utils.show_help(types.FaceDetectNet)) -- cgit v1.2.3-70-g09d2 From bff4e1c50349b0ba7d8e5fab6ce697c0b856f13f Mon Sep 17 00:00:00 2001 From: adamhrv Date: Fri, 4 Jan 2019 13:21:59 +0100 Subject: adding 3D landmarks... --- megapixels/app/processors/face_landmarks.py | 60 ---------------- megapixels/app/processors/face_landmarks_2d.py | 87 +++++++++++++++++++++++ megapixels/app/processors/face_landmarks_3d.py | 51 ++++++++++++-- megapixels/app/settings/types.py | 4 +- megapixels/commands/cv/face_landmark.py | 96 ++++++++++++++++++++++++++ megapixels/commands/cv/face_landmarks_3d.py | 96 -------------------------- megapixels/commands/cv/face_pose.py | 2 +- 7 files changed, 231 insertions(+), 165 deletions(-) delete mode 100644 megapixels/app/processors/face_landmarks.py create mode 100644 megapixels/app/processors/face_landmarks_2d.py create mode 100644 megapixels/commands/cv/face_landmark.py delete mode 100644 megapixels/commands/cv/face_landmarks_3d.py (limited to 'megapixels/app/processors') diff --git a/megapixels/app/processors/face_landmarks.py b/megapixels/app/processors/face_landmarks.py deleted file mode 100644 index dfcb9ee8..00000000 --- a/megapixels/app/processors/face_landmarks.py +++ /dev/null @@ -1,60 +0,0 @@ -import os -from os.path import join -from pathlib import Path - -import cv2 as cv -import numpy as np -import imutils -from app.utils import im_utils, logger_utils -from app.models.bbox import BBox -from app.settings import app_cfg as cfg -from app.settings import types -from app.models.bbox import BBox - -class LandmarksDLIB: - - def __init__(self): - # init dlib - import dlib - self.log = logger_utils.Logger.getLogger() - self.predictor = dlib.shape_predictor(cfg.DIR_MODELS_DLIB_68PT) - - def landmarks(self, im, bbox): - # Draw high-confidence faces - dim = im.shape[:2][::-1] - bbox = bbox.to_dlib() - im_gray = cv.cvtColor(im, cv.COLOR_BGR2GRAY) - landmarks = [[p.x, p.y] for p in self.predictor(im_gray, bbox).parts()] - return landmarks - - -class LandmarksMTCNN: - - # https://github.com/ipazc/mtcnn - # pip install mtcnn - - dnn_size = (400, 400) - - def __init__(self, size=(400,400)): - from mtcnn.mtcnn import MTCNN - self.detector = MTCNN() - - def detect(self, im, opt_size=None, opt_conf_thresh=None, opt_pyramids=None): - '''Detects face using MTCNN and returns (list) of BBox - :param im: (numpy.ndarray) image - :returns list of BBox - ''' - rois = [] - dnn_size = self.dnn_size if opt_size is None else opt_size - im = im_utils.resize(im, width=dnn_size[0], height=dnn_size[1]) - dim = im.shape[:2][::-1] - - # run MTCNN - dets = self.detector.detect_faces(im) - - for det in dets: - rect = det['box'] - keypoints = det['keypoints'] # not using here. see 'face_landmarks.py' - bbox = BBox.from_xywh_dim(*rect, dim) - rois.append(bbox) - return rois \ No newline at end of file diff --git a/megapixels/app/processors/face_landmarks_2d.py b/megapixels/app/processors/face_landmarks_2d.py new file mode 100644 index 00000000..e8ce93c1 --- /dev/null +++ b/megapixels/app/processors/face_landmarks_2d.py @@ -0,0 +1,87 @@ +import os +from os.path import join +from pathlib import Path + +import cv2 as cv +import numpy as np +import imutils +from app.utils import im_utils, logger_utils +from app.models.bbox import BBox +from app.settings import app_cfg as cfg +from app.settings import types +from app.models.bbox import BBox + +class LandmarksFaceAlignment: + + # Estimates 2D facial landmarks + import face_alignment + + def __init__(self, gpu=0): + self.log = logger_utils.Logger.getLogger() + device = f'cuda:{gpu}' if gpu > -1 else 'cpu' + self.fa = face_alignment.FaceAlignment(face_alignment.LandmarksType._2D, device=device, flip_input=True) + + def landmarks(self, im, as_type=str): + '''Calculates the 3D facial landmarks + :param im: (numpy.ndarray) image + :param as_type: (str) or (list) type to return data + ''' + preds = self.fa.get_landmarks(im) + # convert to comma separated ints + # storing data as "[1,2], [3,4]" is larger file size than storing as "1,2,3,4" + # storing a list object in Pandas seems to result in 30% larger CSV files + # TODO optimize this + preds_int = [list(map(int, x)) for x in preds[0]] # list of ints + if as_type is str: + return ','.join([','.join(list(map(str,[x,y]))) for x,y in preds_int]) + else: + return preds_int + + +class LandmarksDLIB: + + def __init__(self): + # init dlib + import dlib + self.log = logger_utils.Logger.getLogger() + self.predictor = dlib.shape_predictor(cfg.DIR_MODELS_DLIB_68PT) + + def landmarks(self, im, bbox): + # Draw high-confidence faces + dim = im.shape[:2][::-1] + bbox = bbox.to_dlib() + im_gray = cv.cvtColor(im, cv.COLOR_BGR2GRAY) + landmarks = [[p.x, p.y] for p in self.predictor(im_gray, bbox).parts()] + return landmarks + + +class LandmarksMTCNN: + + # https://github.com/ipazc/mtcnn + # pip install mtcnn + + dnn_size = (400, 400) + + def __init__(self, size=(400,400)): + from mtcnn.mtcnn import MTCNN + self.detector = MTCNN() + + def landmarks(self, im, opt_size=None, opt_conf_thresh=None, opt_pyramids=None): + '''Detects face using MTCNN and returns (list) of BBox + :param im: (numpy.ndarray) image + :returns list of BBox + ''' + rois = [] + dnn_size = self.dnn_size if opt_size is None else opt_size + im = im_utils.resize(im, width=dnn_size[0], height=dnn_size[1]) + dim = im.shape[:2][::-1] + + # run MTCNN + dets = self.detector.detect_faces(im) + + for det in dets: + rect = det['box'] + keypoints = det['keypoints'] # not using here. see 'face_landmarks.py' + bbox = BBox.from_xywh_dim(*rect, dim) + rois.append(bbox) + return rois \ No newline at end of file diff --git a/megapixels/app/processors/face_landmarks_3d.py b/megapixels/app/processors/face_landmarks_3d.py index 28aff592..3663364c 100644 --- a/megapixels/app/processors/face_landmarks_3d.py +++ b/megapixels/app/processors/face_landmarks_3d.py @@ -13,24 +13,63 @@ from app.settings import app_cfg as cfg from app.settings import types +class FaceLandmarks2D: + + # Estimates 2D facial landmarks + import face_alignment + + def __init__(self, gpu=0): + self.log = logger_utils.Logger.getLogger() + device = f'cuda:{gpu}' if gpu > -1 else 'cpu' + self.fa = face_alignment.FaceAlignment(face_alignment.LandmarksType._2D, device=device, flip_input=True) + + def landmarks(self, im, as_type=str): + '''Calculates the 3D facial landmarks + :param im: (numpy.ndarray) image + :param as_type: (str) or (list) type to return data + ''' + preds = self.fa.get_landmarks(im) + # convert to comma separated ints + # storing data as "[1,2], [3,4]" is larger file size than storing as "1,2,3,4" + # storing a list object in Pandas seems to result in 30% larger CSV files + # TODO optimize this + preds_int = [list(map(int, x)) for x in preds[0]] # list of ints + if as_type is str: + return ','.join([','.join(list(map(str,[x,y]))) for x,y in preds_int]) + else + return preds_int + class FaceLandmarks3D: # Estimates 3D facial landmarks import face_alignment - from skimage import io - def __init__(self): + def __init__(self, gpu=0): self.log = logger_utils.Logger.getLogger() - self.fa = face_alignment.FaceAlignment(face_alignment.LandmarksType._2D, flip_input=False) + device = f'cuda:{gpu}' if gpu > -1 else 'cpu' + self.fa = face_alignment.FaceAlignment(face_alignment.LandmarksType._3D, device=device, flip_input=False) - def landmarks(self, im): + def landmarks(self, im, as_type=str): + '''Calculates the 3D facial landmarks + :param im: (numpy.ndarray) image + :param as_type: (str) or (list) type to return data + ''' preds = self.fa.get_landmarks(im) - return preds + # convert to comma separated ints + # storing data as "[1,2], [3,4]" is larger file size than storing as "1,2,3,4" + # storing a list object in Pandas seems to result in 30% larger CSV files + # TODO optimize this + preds_int = [list(map(int, x)) for x in preds[0]] # list of ints + if as_type is str: + return ','.join([','.join(list(map(str,[x,y]))) for x,y in preds_int]) + else + return preds_int def draw(self, im): '''draws landmarks in 3d scene''' + # TODO ''' import face_alignment import numpy as np @@ -74,4 +113,4 @@ class FaceLandmarks3D: ax.set_xlim(ax.get_xlim()[::-1]) plt.show() ''' - return False \ No newline at end of file + return im \ No newline at end of file diff --git a/megapixels/app/settings/types.py b/megapixels/app/settings/types.py index 0805c5bd..c2e2caf7 100644 --- a/megapixels/app/settings/types.py +++ b/megapixels/app/settings/types.py @@ -45,8 +45,8 @@ class LogLevel(Enum): # -------------------------------------------------------------------- class Metadata(Enum): - IDENTITY, FILE_RECORD, FACE_VECTOR, FACE_POSE, FACE_ROI, FACE_LANDMARKS_68, \ - FACE_LANDMARKS_3D = range(7) + IDENTITY, FILE_RECORD, FACE_VECTOR, FACE_POSE, FACE_ROI, FACE_LANDMARKS_2D_68, \ + FACE_LANDMARKS_3D_68 = range(7) class Dataset(Enum): LFW, VGG_FACE2, MSCELEB, UCCS, UMD_FACES = range(5) diff --git a/megapixels/commands/cv/face_landmark.py b/megapixels/commands/cv/face_landmark.py new file mode 100644 index 00000000..03ef8fc2 --- /dev/null +++ b/megapixels/commands/cv/face_landmark.py @@ -0,0 +1,96 @@ +""" + +""" + +import click + +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_dirs_in', required=True, multiple=True, + help='Input directory') +@click.option('-o', '--output', 'opt_fp_out', required=True, + help='Output CSV') +@click.option('-e', '--ext', 'opt_ext', + default='jpg', type=click.Choice(['jpg', 'png']), + help='File glob ext') +@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('--slice', 'opt_slice', type=(int, int), default=(None, None), + help='Slice list of files') +@click.option('--recursive/--no-recursive', 'opt_recursive', is_flag=True, default=False, + help='Use glob recursion (slower)') +@click.option('-f', '--force', 'opt_force', is_flag=True, + help='Force overwrite file') +@click.pass_context +def cli(ctx, opt_dirs_in, opt_fp_out, opt_ext, opt_size, opt_gpu, opt_slice, + opt_recursive, opt_force): + """Converts face imges to 3D landmarks""" + + 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 face_alignment import FaceAlignment, LandmarksType + from skimage import io + + from app.utils import logger_utils, file_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 + + device = 'cuda' if opt_gpu > -1 else 'cpu' + fa = FaceAlignment(LandmarksType._3D, flip_input=False, device=device) + + # get list of files to process + fp_ims = [] + for opt_dir_in in opt_dirs_in: + if opt_recursive: + fp_glob = join(opt_dir_in, '**/*.{}'.format(opt_ext)) + fp_ims += glob(fp_glob, recursive=True) + else: + fp_glob = join(opt_dir_in, '*.{}'.format(opt_ext)) + fp_ims += glob(fp_glob) + log.debug(fp_glob) + + + if opt_slice: + fp_ims = fp_ims[opt_slice[0]:opt_slice[1]] + log.debug('processing {:,} files'.format(len(fp_ims))) + + + data = {} + + for fp_im in tqdm(fp_ims): + fpp_im = Path(fp_im) + im = io.imread(fp_im) + preds = fa.get_landmarks(im) + if preds and len(preds) > 0: + data[fpp_im.name] = preds[0].tolist() + + # save date + file_utils.mkdirs(opt_fp_out) + + file_utils.write_json(data, opt_fp_out, verbose=True) \ No newline at end of file diff --git a/megapixels/commands/cv/face_landmarks_3d.py b/megapixels/commands/cv/face_landmarks_3d.py deleted file mode 100644 index 03ef8fc2..00000000 --- a/megapixels/commands/cv/face_landmarks_3d.py +++ /dev/null @@ -1,96 +0,0 @@ -""" - -""" - -import click - -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_dirs_in', required=True, multiple=True, - help='Input directory') -@click.option('-o', '--output', 'opt_fp_out', required=True, - help='Output CSV') -@click.option('-e', '--ext', 'opt_ext', - default='jpg', type=click.Choice(['jpg', 'png']), - help='File glob ext') -@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('--slice', 'opt_slice', type=(int, int), default=(None, None), - help='Slice list of files') -@click.option('--recursive/--no-recursive', 'opt_recursive', is_flag=True, default=False, - help='Use glob recursion (slower)') -@click.option('-f', '--force', 'opt_force', is_flag=True, - help='Force overwrite file') -@click.pass_context -def cli(ctx, opt_dirs_in, opt_fp_out, opt_ext, opt_size, opt_gpu, opt_slice, - opt_recursive, opt_force): - """Converts face imges to 3D landmarks""" - - 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 face_alignment import FaceAlignment, LandmarksType - from skimage import io - - from app.utils import logger_utils, file_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 - - device = 'cuda' if opt_gpu > -1 else 'cpu' - fa = FaceAlignment(LandmarksType._3D, flip_input=False, device=device) - - # get list of files to process - fp_ims = [] - for opt_dir_in in opt_dirs_in: - if opt_recursive: - fp_glob = join(opt_dir_in, '**/*.{}'.format(opt_ext)) - fp_ims += glob(fp_glob, recursive=True) - else: - fp_glob = join(opt_dir_in, '*.{}'.format(opt_ext)) - fp_ims += glob(fp_glob) - log.debug(fp_glob) - - - if opt_slice: - fp_ims = fp_ims[opt_slice[0]:opt_slice[1]] - log.debug('processing {:,} files'.format(len(fp_ims))) - - - data = {} - - for fp_im in tqdm(fp_ims): - fpp_im = Path(fp_im) - im = io.imread(fp_im) - preds = fa.get_landmarks(im) - if preds and len(preds) > 0: - data[fpp_im.name] = preds[0].tolist() - - # save date - file_utils.mkdirs(opt_fp_out) - - file_utils.write_json(data, opt_fp_out, verbose=True) \ No newline at end of file diff --git a/megapixels/commands/cv/face_pose.py b/megapixels/commands/cv/face_pose.py index 9979ad34..4e35210c 100644 --- a/megapixels/commands/cv/face_pose.py +++ b/megapixels/commands/cv/face_pose.py @@ -57,7 +57,7 @@ def cli(ctx, opt_fp_in, opt_fp_out, opt_dir_media, opt_data_store, opt_dataset, from app.models.bbox import BBox from app.utils import logger_utils, file_utils, im_utils - from app.processors.face_landmarks import LandmarksDLIB + from app.processors.face_landmarks_2d import LandmarksDLIB from app.processors.face_pose import FacePoseDLIB from app.models.data_store import DataStore -- cgit v1.2.3-70-g09d2