summaryrefslogtreecommitdiff
path: root/megapixels/commands/cv/face_pose.py
diff options
context:
space:
mode:
authoradamhrv <adam@ahprojects.com>2019-01-06 17:16:18 +0100
committeradamhrv <adam@ahprojects.com>2019-01-06 17:16:18 +0100
commit4bcb82c0f295d79d3d247252e7e98b2d986ae821 (patch)
treea51105698c46ecfcb0a09c5ba294f9d9ffa43e7a /megapixels/commands/cv/face_pose.py
parent2efde746810a0264ad2cf09dc9b003bfcd17a4d5 (diff)
externalize drawing, cleanup
Diffstat (limited to 'megapixels/commands/cv/face_pose.py')
-rw-r--r--megapixels/commands/cv/face_pose.py76
1 files changed, 44 insertions, 32 deletions
diff --git a/megapixels/commands/cv/face_pose.py b/megapixels/commands/cv/face_pose.py
index 4e35210c..70ea1f30 100644
--- a/megapixels/commands/cv/face_pose.py
+++ b/megapixels/commands/cv/face_pose.py
@@ -1,4 +1,6 @@
"""
+NB: This only works with the DLIB 68-point landmarks.
+
Converts ROIs to pose: yaw, roll, pitch
pitch: looking down or up in yes gesture
roll: tilting head towards shoulder
@@ -6,6 +8,13 @@ yaw: twisting head left to right in no gesture
"""
+"""
+TODO
+- check compatibility with MTCNN 68 point detector
+- improve accuracy by using MTCNN 5-point
+- refer to https://github.com/jerryhouuu/Face-Yaw-Roll-Pitch-from-Pose-Estimation-using-OpenCV/
+"""
+
import click
from app.settings import types
@@ -19,7 +28,7 @@ from app.settings import app_cfg as cfg
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,
@@ -56,8 +65,8 @@ def cli(ctx, opt_fp_in, opt_fp_out, opt_dir_media, opt_data_store, opt_dataset,
import pandas as pd
from app.models.bbox import BBox
- from app.utils import logger_utils, file_utils, im_utils
- from app.processors.face_landmarks_2d import LandmarksDLIB
+ from app.utils import logger_utils, file_utils, im_utils, display_utils, draw_utils
+ from app.processors.face_landmarks import Dlib2D_68
from app.processors.face_pose import FacePoseDLIB
from app.models.data_store import DataStore
@@ -77,9 +86,11 @@ def cli(ctx, opt_fp_in, opt_fp_out, opt_dir_media, opt_data_store, opt_dataset,
# init face processors
face_pose = FacePoseDLIB()
- face_landmarks = LandmarksDLIB()
+ face_landmarks = Dlib2D_68()
+
+ # -------------------------------------------------
+ # load data
- # load filepath data
fp_record = data_store.metadata(types.Metadata.FILE_RECORD)
df_record = pd.read_csv(fp_record).set_index('index')
# load ROI data
@@ -93,59 +104,60 @@ def cli(ctx, opt_fp_in, opt_fp_out, opt_dir_media, opt_data_store, opt_dataset,
log.debug('processing {:,} groups'.format(len(df_img_groups)))
# store poses and convert to DataFrame
- poses = []
+ results = []
+ # -------------------------------------------------
# iterate groups with file/record index as key
for record_index, df_img_group in tqdm(df_img_groups):
- # make fp
- ds_record = df_record.iloc[record_index]
- fp_im = data_store.face(ds_record.subdir, ds_record.fn, ds_record.ext)
+
+ # access the file_record
+ file_record = df_record.iloc[record_index] # pands.DataSeries
+
+ # load image
+ fp_im = data_store.face(file_record.subdir, file_record.fn, file_record.ext)
im = cv.imread(fp_im)
+ im_resized = im_utils.resize(im, width=opt_size[0], height=opt_size[1])
+
# iterate image group dataframe with roi index as key
for roi_index, df_img in df_img_group.iterrows():
+
# get bbox
x, y, w, h = df_img.x, df_img.y, df_img.w, df_img.h
- dim = (ds_record.width, ds_record.height)
- #dim = im.shape[:2][::-1]
+ #dim = (file_record.width, file_record.height)
+ dim = im_resized.shape[:2][::-1]
bbox = BBox.from_xywh(x, y, w, h).to_dim(dim)
+
# get pose
- landmarks = face_landmarks.landmarks(im, bbox)
+ landmarks = face_landmarks.landmarks(im_resized, bbox)
pose_data = face_pose.pose(landmarks, dim)
#pose_degrees = pose_data['degrees'] # only keep the degrees data
#pose_degrees['points_nose'] = pose_data
- # use the project point data if display flag set
+
+ # draw landmarks if optioned
if opt_display:
- dst = im.copy()
- face_pose.draw_pose(dst, pose_data['point_nose'], pose_data['points'])
- face_pose.draw_degrees(dst, pose_data)
- # display to cv window
- cv.imshow('', dst)
- 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
+ draw_utils.draw_pose(im_resized, pose_data['point_nose'], pose_data['points'])
+ draw_utils.draw_degrees(im_resized, pose_data)
+ cv.imshow('', im_resized)
+ display_utils.handle_keyboard()
# add image index and append to result CSV data
pose_data['roi_index'] = roi_index
for k, v in pose_data['points'].items():
pose_data[f'point_{k}_x'] = v[0][0] / dim[0]
pose_data[f'point_{k}_y'] = v[0][1] / dim[1]
+
+ # rearrange data structure for DataFrame
pose_data.pop('points')
pose_data['point_nose_x'] = pose_data['point_nose'][0] / dim[0]
pose_data['point_nose_y'] = pose_data['point_nose'][1] / dim[1]
pose_data.pop('point_nose')
- poses.append(pose_data)
+ results.append(pose_data)
- # create dataframe
+ # create DataFrame and save to CSV
file_utils.mkdirs(fp_out)
- df = pd.DataFrame.from_dict(poses)
- # save date
+ df = pd.DataFrame.from_dict(results)
df.index.name = 'index'
df.to_csv(fp_out)
+
# save script
- cmd_line = ' '.join(sys.argv)
- file_utils.write_text(cmd_line, '{}.sh'.format(fp_out)) \ No newline at end of file
+ file_utils.write_text(' '.join(sys.argv), '{}.sh'.format(fp_out)) \ No newline at end of file