From b0e78ceffcf1647072f387f4da213e73bdfbc71b Mon Sep 17 00:00:00 2001 From: Guillaume HEUSCH <guillaume.heusch@idiap.ch> Date: Mon, 1 May 2017 15:12:25 +0200 Subject: [PATCH] [extract eyes] made scripts to extract eyes center from detected landmarks --- .../scripts/eyes_center_from_landmarks.py | 408 ++++++++++++++++++ setup.py | 1 + 2 files changed, 409 insertions(+) create mode 100644 bob/db/fargo/scripts/eyes_center_from_landmarks.py diff --git a/bob/db/fargo/scripts/eyes_center_from_landmarks.py b/bob/db/fargo/scripts/eyes_center_from_landmarks.py new file mode 100644 index 0000000..ef93ef4 --- /dev/null +++ b/bob/db/fargo/scripts/eyes_center_from_landmarks.py @@ -0,0 +1,408 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Guillaume HEUSCH <guillaume.heusch@idiap.ch> +# Mon 21 Nov 08:25:54 CET 2016 + +"""Eyes center extractor for the FARGO images (%(version)s) + +Usage: + %(prog)s [--ldmdir=<path>] [--eyesdir=<path>] [--imagesdir=<path>] + [--verbose ...] [--plot] [--log=<string>] + +Options: + -h, --help Show this screen. + -V, --version Show version. + -l, --ldmdir=<path> The path to the landmarks on your disk. + -e, --eyesdir=<path> Where to store saved images. + -i, --imagesdir=<path> Where the images are stored. + --log=<string> Log filename [default: logs.txt] + -v, --verbose Increase the verbosity (may appear multiple times). + -P, --plot Show some stuff + +Example: + + To get the eyes center, do + + $ %(prog)s --ldmdir path/to/database + +See '%(prog)s --help' for more information. + +""" + +import os +import sys +import pkg_resources + +import logging +__logging_format__='[%(levelname)s] %(message)s' +logging.basicConfig(format=__logging_format__) +logger = logging.getLogger("extract_log") + +from docopt import docopt + +version = pkg_resources.require('bob.db.fargo')[0].version + +import numpy + +import bob.io.base +import bob.io.image +import bob.ip.draw + + +def get_frame(image_dir, index): + """ get_frame(image_dir, index) -> frame + + This function gets the frame that corresponds to the landmarks. + + **Parameters** + + ``image_dir`` (path): + The dir continaing the image + + ``index`` (int): + The index of the image + + **Returns** + + ``frame`` (numpy array): + The frame where the annotation have been made + """ + image_file = os.path.join(image_dir, '{:0>2d}.png'.format(index)) + if os.path.isfile(image_file): + frame = bob.io.base.load(image_file) + return frame + + +def is_annotation_complete(landmarks): + """ is_annotation_complete(landmarks) -> True or False + + This function checks if the landmarks read from the provided + file contain what we need to infer eyes center (i.e. eyes corner) + + **Parameters** + + ``landmarks`` (dict): + The landmarks, read from a txt file and stored as a dict + + **Returns** + + ``bool`` (boolean): + True if all eyes corners are present, False otherwise. + """ + if '1' not in landmarks.keys() or '2' not in landmarks.keys() or '3' not in landmarks.keys() or '4' not in landmarks.keys(): + return False + return True + + +def get_eyes_center(landmarks): + """ get_eyes_center(landmarks) -> eyes_center + + This function computes the position of the eyes center, + based on eyes corners. + + Note that left and right are defined wrt the imaged subject. + + **Parameters** + + ``landmarks`` (dict): + The landmarks, read from a txt file and stored as a dict + + **Returns** + + ``eyes_center`` (tuple): + Tuple containing the (x, y) position of the right and left eye. + """ + reye_x = int(0.5 * (landmarks['1'][1] + landmarks['2'][1])) + reye_y = int(0.5 * (landmarks['1'][0] + landmarks['2'][0])) + leye_x = int(0.5 * (landmarks['3'][1] + landmarks['4'][1])) + leye_y = int(0.5 * (landmarks['3'][0] + landmarks['4'][0])) + return (reye_x, reye_y, leye_x, leye_y) + + +def draw_eyes_center(frame, positions): + """ draw_eyes_center(frame, positions) -> frame + + This function draws the computed eyes center on the provided image. + + **Parameters** + + ``frame`` (numpy array): + The frame on which to draw eyes center. + + ``positions`` (tuple): + The position of the center of both eyes. + """ + reye_x = positions[0] + reye_y = positions[1] + leye_x = positions[2] + leye_y = positions[3] + if frame.shape[0] == 3: + bob.ip.draw.cross(frame, (reye_y, reye_x), 4, (255,0,0)) + bob.ip.draw.cross(frame, (leye_y, leye_x), 4, (255,0,0)) + else: + bob.ip.draw.cross(frame, (reye_y, reye_x), 4, (255)) + bob.ip.draw.cross(frame, (leye_y, leye_x), 4, (255)) + return frame + + +def draw_landmarks(frame, landmarks): + """ draw_landmarks(frame, landmarks) -> frame + + This function draws both the original landmarks (provided in the file) + and the projected ones. + + **Parameters** + + ``frame`` (numpy array): + The frame on which to draw eyes center. + + ``landmarks`` (dict): + The landmarks + """ + for i in landmarks.keys(): + if frame.shape[0] == 3: + bob.ip.draw.plus(frame, (landmarks[i][0], landmarks[i][1]), 4, (0,255,0)) + else: + bob.ip.draw.plus(frame, (landmarks[i][0], landmarks[i][1]), 4, (255)) + return frame + + +def get_landmarks(annotation_file): + """ get_landmarks(annotation_file) -> landmarks + + This function reads landmarks from a file and load + them into a dictionary. + + + Note: left and right are defined in terms of subject + Landmarks: + 1 right corner of right eye + 2 left corner of right eye + 3 right corner of left eye + 4 left corner of left eye + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + + **Parameters** + + ``annotated_file`` (path): + The path to the file containing the landmarks. + + **Returns** + + ``landmarks`` (dict): + The landmarks. + """ + with open(annotation_file, "r") as c: + landmarks = {} + for line in c: + line = line.rstrip() + ints = line.split() + landmarks[ints[0]] = ((int(ints[2]), int(ints[1]))) + return landmarks + +def write_eyes_pos(eyes, eyes_dir): + """ write_eyes_pos(eyes, eyes_dir) + + This function write the eyes center in text file(s). + + **Parameters** + + ``eyes`` (tuple): + tuple containing the (x,y) coordinates of the eyes center. + + ``eyes_dir`` (path): + The path to the dir where the file(s) are written. + """ + if not os.path.isdir(eyes_dir): + os.makedirs(eyes_dir) + for i in range(0, 10): + eyes_filename = os.path.join(eyes_dir, '{:0>2d}.pos'.format(i)) + eyes_file = open(eyes_filename, 'w') + eyes_file.write('{0} {1} {2} {3}'.format(eyes[0], eyes[1], eyes[2], eyes[3])) + eyes_file.close() + + +def annotations_exist(annotation_dir): + """annotations_exist(annotation_dir) -> [True, False] + + This function checks if the annotation (i.e. eyes center) already exists + + **Parameters** + + ``annotation_dir`` (path): + The directory where the annotations should be saved. + + **Returns** + + ``bool`` (boolean): + True if all annotations exist, False otherwise. + """ + for i in range(0,10): + annotation_file = os.path.join(annotation_dir, '{:0>2d}.pos'.format(i)) + if not os.path.isfile(annotation_file): + return False + return True + + +def main(user_input=None): + """ + Main function to extract eyes center from existing annotations. + """ + + # Parse the command-line arguments + if user_input is not None: + arguments = user_input + else: + arguments = sys.argv[1:] + + prog = os.path.basename(sys.argv[0]) + completions = dict(prog=prog,version=version,) + args = docopt(__doc__ % completions,argv=arguments, + version='Eyes position extractor (%s)' % version,) + + # if the user wants more verbosity, lowers the logging level + if args['--verbose'] == 1: logging.getLogger("extract_log").setLevel(logging.INFO) + elif args['--verbose'] >= 2: logging.getLogger("extract_log").setLevel(logging.DEBUG) + + if args['--ldmdir'] is None: + logger.warning("You should provide a valid path to the landmarks") + sys.exit() + + base_dir = args['--ldmdir'] + if not os.path.isdir(args['--eyesdir']): + os.mkdir(args['--eyesdir']) + + logfile = open(args['--log'], 'w') + + # to compute various stats + total_counter = 0 + inexisting_counter = 0 + incomplete_counter = 0 + projection_counter = 0 + heuristic_counter = 0 + + # go through the subjects + for subject in os.listdir(base_dir): + + sessions = ['controlled', 'dark', 'outdoor'] + # small hack to process FdV subjects ... + if int(subject) >= 129: + sessions = ['fdv'] + + for session in sessions: + session_dir = os.path.abspath(os.path.join(base_dir, subject, session)) + + for condition in ['SR300-laptop', 'SR300-mobile']: + + for recording in ['0', '1']: + logger.debug("===== Subject {0}, session {1}, device {2}, recording {3} ...".format(subject, session, condition, recording)) + + # create directories to save the extracted annotations + if not os.path.isdir(os.path.join(args['--eyesdir'], subject, session, condition, recording)): + os.makedirs(os.path.join(args['--eyesdir'], subject, session, condition, recording)) + if not os.path.isdir(os.path.join(args['--eyesdir'], subject, session, condition, recording, 'color')): + os.makedirs(os.path.join(args['--eyesdir'], subject, session, condition, recording, 'color')) + if not os.path.isdir(os.path.join(args['--eyesdir'], subject, session, condition, recording, 'ir')): + os.makedirs(os.path.join(args['--eyesdir'], subject, session, condition, recording, 'ir')) + if not os.path.isdir(os.path.join(args['--eyesdir'], subject, session, condition, recording, 'depth')): + os.makedirs(os.path.join(args['--eyesdir'], subject, session, condition, recording, 'depth')) + + # the directories - input + recording_dir = os.path.join(session_dir, condition, recording) + color_dir = os.path.join(recording_dir, 'color') + ir_dir = os.path.join(recording_dir, 'ir') + + # the directories - output + color_eyes_dir = os.path.join(args['--eyesdir'], subject, session, condition, recording, 'color') + ir_eyes_dir = os.path.join(args['--eyesdir'], subject, session, condition, recording, 'ir') + depth_eyes_dir = os.path.join(args['--eyesdir'], subject, session, condition, recording, 'depth') + + # check if annotations for this recording already exists + if annotations_exist(color_eyes_dir) and annotations_exist(ir_eyes_dir) and annotations_exist(depth_eyes_dir): + logger.warn('Existing annotations for {0}'.format(recording_dir)) + continue + + # loop on the images extracted from this sequences + for i in range(0,10): + + # read the original landmarks - color + color_ldm_file = os.path.join(color_dir, '{:0>2d}.pos'.format(i)) + try: + color_landmarks = get_landmarks(color_ldm_file) + except IOError: + logger.warn("No color annotations for recording {0}".format(recording_dir)) + logfile.write('[NO ANNOTATIONS] ' + color_dir + '\n') + inexisting_counter += 1 + + # read the original landmarks - ir + ir_ldm_file = os.path.join(ir_dir, '{:0>2d}.pos'.format(i)) + try: + ir_landmarks = get_landmarks(ir_ldm_file) + except IOError: + logger.warn("No ir annotations for recording {0}".format(recording_dir)) + logfile.write('[NO ANNOTATIONS] ' + ir_dir + '\n') + inexisting_counter += 1 + + # check if we have all we need (possibly after re-projection) + if is_annotation_complete(color_landmarks) and is_annotation_complete(ir_landmarks): + + # get the eyes center + color_eyes = get_eyes_center(color_landmarks) + ir_eyes = get_eyes_center(ir_landmarks) + + # and save the file(s) - note that ir and depth are the same + write_eyes_pos(color_eyes, color_eyes_dir) + write_eyes_pos(ir_eyes, ir_eyes_dir) + write_eyes_pos(ir_eyes, depth_eyes_dir) + + # plot stuff if asked for + if bool(args['--plot']): + if args['--imagesdir'] is None: + logger.warn("You should provide an image directory if you want to plot something ...") + else: + color_frame_dir = os.path.join(args['--imagesdir'], subject, session, condition, recording, 'color') + color_frame = get_frame(color_frame_dir, i) + display_color = draw_eyes_center(color_frame, color_eyes) + ir_frame_dir = os.path.join(args['--imagesdir'], subject, session, condition, recording, 'ir') + ir_frame = get_frame(ir_frame_dir, i) + display_ir = draw_eyes_center(ir_frame, ir_eyes) + from matplotlib import pyplot + f, axarr = pyplot.subplots(1, 2) + pyplot.suptitle('Inferred eyes center') + axarr[0].imshow(numpy.rollaxis(numpy.rollaxis(display_color, 2),2)) + axarr[0].set_title("Color") + axarr[1].imshow(display_ir, cmap='gray') + axarr[1].set_title("NIR") + pyplot.show() + + if args['--verbose'] >= 2: + display_color = draw_landmarks(color_frame, color_landmarks) + display_ir = draw_landmarks(ir_frame, ir_landmarks) + f, axarr = pyplot.subplots(1, 2) + pyplot.suptitle('Landmarks') + axarr[0].imshow(numpy.rollaxis(numpy.rollaxis(display_color, 2),2)) + axarr[0].set_title("Color") + axarr[1].imshow(display_ir, cmap='gray') + axarr[1].set_title("NIR") + pyplot.show() + + total_counter += 1 + + logger.info("Processed {0} sequences".format(total_counter)) + logger.info("\t {0} had no annotations".format(inexisting_counter)) + logger.info("\t {0} needed reprojection".format(projection_counter)) + logger.info("\t {0} where incomplete".format(incomplete_counter)) + logger.info("\t {0} needed heuristic (incomplete after reprojection)".format(heuristic_counter)) + + logfile.close() diff --git a/setup.py b/setup.py index b005eeb..f3d4f8b 100644 --- a/setup.py +++ b/setup.py @@ -38,6 +38,7 @@ setup( 'make_public_lists.py = bob.db.fargo.scripts.make_public_lists:main', 'make_pose_lists.py = bob.db.fargo.scripts.make_pose_lists:main', 'extract_eyes_center.py = bob.db.fargo.scripts.extract_eyes_center:main', + 'eyes_center_from_landmarks.py = bob.db.fargo.scripts.eyes_center_from_landmarks:main', 'convert_ir_and_depth.py = bob.db.fargo.scripts.convert_ir_and_depth:main', 'reencode_color.py = bob.db.fargo.scripts.reencode_color:main', 'detect_faces_fargo.py = bob.db.fargo.scripts.detect_faces_fargo:main', -- GitLab