2016-04-28 21:40:36 +02:00
///////////////////////////////////////////////////////////////////////////////
2017-05-09 03:36:23 +02:00
// Copyright (C) 2017, Carnegie Mellon University and University of Cambridge,
2016-04-28 21:40:36 +02:00
// all rights reserved.
//
2017-05-09 03:36:23 +02:00
// ACADEMIC OR NON-PROFIT ORGANIZATION NONCOMMERCIAL RESEARCH USE ONLY
2016-04-28 21:40:36 +02:00
//
2017-05-09 03:36:23 +02:00
// BY USING OR DOWNLOADING THE SOFTWARE, YOU ARE AGREEING TO THE TERMS OF THIS LICENSE AGREEMENT.
// IF YOU DO NOT AGREE WITH THESE TERMS, YOU MAY NOT USE OR DOWNLOAD THE SOFTWARE.
//
// License can be found in OpenFace-license.txt
2016-04-28 21:40:36 +02:00
// * Any publications arising from the use of this software, including but
// not limited to academic journal and conference publications, technical
// reports and manuals, must cite at least one of the following works:
//
// OpenFace: an open source facial behavior analysis toolkit
// Tadas Baltru<72> aitis, Peter Robinson, and Louis-Philippe Morency
// in IEEE Winter Conference on Applications of Computer Vision, 2016
//
// Rendering of Eyes for Eye-Shape Registration and Gaze Estimation
// Erroll Wood, Tadas Baltru<72> aitis, Xucong Zhang, Yusuke Sugano, Peter Robinson, and Andreas Bulling
// in IEEE International. Conference on Computer Vision (ICCV), 2015
//
// Cross-dataset learning and person-speci?c normalisation for automatic Action Unit detection
// Tadas Baltru<72> aitis, Marwa Mahmoud, and Peter Robinson
// in Facial Expression Recognition and Analysis Challenge,
// IEEE International Conference on Automatic Face and Gesture Recognition, 2015
//
// Constrained Local Neural Fields for robust facial landmark detection in the wild.
// Tadas Baltru<72> aitis, Peter Robinson, and Louis-Philippe Morency.
// in IEEE Int. Conference on Computer Vision Workshops, 300 Faces in-the-Wild Challenge, 2013.
//
///////////////////////////////////////////////////////////////////////////////
// FaceLandmarkImg.cpp : Defines the entry point for the console application for detecting landmarks in images.
# include "LandmarkCoreIncludes.h"
// System includes
# include <fstream>
// OpenCV includes
# include <opencv2/core/core.hpp>
# include <opencv2/highgui/highgui.hpp>
# include <opencv2/imgproc.hpp>
// Boost includes
# include <filesystem.hpp>
# include <filesystem/fstream.hpp>
# include <dlib/image_processing/frontal_face_detector.h>
# include <tbb/tbb.h>
# include <FaceAnalyser.h>
# include <GazeEstimation.h>
2017-11-14 20:59:08 +01:00
# include <ImageCapture.h>
# include <Visualizer.h>
# include <VisualizationUtils.h>
# include <RecorderOpenFace.h>
# include <RecorderOpenFaceParameters.h>
2016-12-31 17:52:30 +01:00
# ifndef CONFIG_DIR
# define CONFIG_DIR "~"
# endif
2016-04-28 21:40:36 +02:00
using namespace std ;
vector < string > get_arguments ( int argc , char * * argv )
{
vector < string > arguments ;
for ( int i = 0 ; i < argc ; + + i )
{
arguments . push_back ( string ( argv [ i ] ) ) ;
}
return arguments ;
}
2017-11-08 22:13:02 +01:00
// TODO rem
2016-04-28 21:40:36 +02:00
void create_display_image ( const cv : : Mat & orig , cv : : Mat & display_image , LandmarkDetector : : CLNF & clnf_model )
{
// Draw head pose if present and draw eye gaze as well
// preparing the visualisation image
display_image = orig . clone ( ) ;
// Creating a display image
cv : : Mat xs = clnf_model . detected_landmarks ( cv : : Rect ( 0 , 0 , 1 , clnf_model . detected_landmarks . rows / 2 ) ) ;
cv : : Mat ys = clnf_model . detected_landmarks ( cv : : Rect ( 0 , clnf_model . detected_landmarks . rows / 2 , 1 , clnf_model . detected_landmarks . rows / 2 ) ) ;
double min_x , max_x , min_y , max_y ;
cv : : minMaxLoc ( xs , & min_x , & max_x ) ;
cv : : minMaxLoc ( ys , & min_y , & max_y ) ;
double width = max_x - min_x ;
double height = max_y - min_y ;
int minCropX = max ( ( int ) ( min_x - width / 3.0 ) , 0 ) ;
int minCropY = max ( ( int ) ( min_y - height / 3.0 ) , 0 ) ;
int widthCrop = min ( ( int ) ( width * 5.0 / 3.0 ) , display_image . cols - minCropX - 1 ) ;
int heightCrop = min ( ( int ) ( height * 5.0 / 3.0 ) , display_image . rows - minCropY - 1 ) ;
double scaling = 350.0 / widthCrop ;
// first crop the image
display_image = display_image ( cv : : Rect ( ( int ) ( minCropX ) , ( int ) ( minCropY ) , ( int ) ( widthCrop ) , ( int ) ( heightCrop ) ) ) ;
// now scale it
cv : : resize ( display_image . clone ( ) , display_image , cv : : Size ( ) , scaling , scaling ) ;
// Make the adjustments to points
xs = ( xs - minCropX ) * scaling ;
ys = ( ys - minCropY ) * scaling ;
cv : : Mat shape = clnf_model . detected_landmarks . clone ( ) ;
xs . copyTo ( shape ( cv : : Rect ( 0 , 0 , 1 , clnf_model . detected_landmarks . rows / 2 ) ) ) ;
ys . copyTo ( shape ( cv : : Rect ( 0 , clnf_model . detected_landmarks . rows / 2 , 1 , clnf_model . detected_landmarks . rows / 2 ) ) ) ;
// Do the shifting for the hierarchical models as well
for ( size_t part = 0 ; part < clnf_model . hierarchical_models . size ( ) ; + + part )
{
cv : : Mat xs = clnf_model . hierarchical_models [ part ] . detected_landmarks ( cv : : Rect ( 0 , 0 , 1 , clnf_model . hierarchical_models [ part ] . detected_landmarks . rows / 2 ) ) ;
cv : : Mat ys = clnf_model . hierarchical_models [ part ] . detected_landmarks ( cv : : Rect ( 0 , clnf_model . hierarchical_models [ part ] . detected_landmarks . rows / 2 , 1 , clnf_model . hierarchical_models [ part ] . detected_landmarks . rows / 2 ) ) ;
xs = ( xs - minCropX ) * scaling ;
ys = ( ys - minCropY ) * scaling ;
cv : : Mat shape = clnf_model . hierarchical_models [ part ] . detected_landmarks . clone ( ) ;
xs . copyTo ( shape ( cv : : Rect ( 0 , 0 , 1 , clnf_model . hierarchical_models [ part ] . detected_landmarks . rows / 2 ) ) ) ;
ys . copyTo ( shape ( cv : : Rect ( 0 , clnf_model . hierarchical_models [ part ] . detected_landmarks . rows / 2 , 1 , clnf_model . hierarchical_models [ part ] . detected_landmarks . rows / 2 ) ) ) ;
}
2017-11-14 21:36:58 +01:00
//LandmarkDetector::Draw(display_image, clnf_model);
2016-04-28 21:40:36 +02:00
}
int main ( int argc , char * * argv )
{
//Convert arguments to more convenient vector form
vector < string > arguments = get_arguments ( argc , argv ) ;
2017-11-14 20:59:08 +01:00
// Prepare for image reading
Utilities : : ImageCapture image_reader ;
2016-04-28 21:40:36 +02:00
2017-11-14 20:59:08 +01:00
// The sequence reader chooses what to open based on command line arguments provided
if ( ! image_reader . Open ( arguments ) )
2016-04-28 21:40:36 +02:00
{
2017-11-14 20:59:08 +01:00
cout < < " Could not open any images " < < endl ;
return 1 ;
2016-04-28 21:40:36 +02:00
}
2017-11-14 20:59:08 +01:00
// Load the models if images found
LandmarkDetector : : FaceModelParameters det_parameters ( arguments ) ;
2016-04-28 21:40:36 +02:00
// The modules that are being used for tracking
cout < < " Loading the model " < < endl ;
2017-11-14 20:59:08 +01:00
LandmarkDetector : : CLNF face_model ( det_parameters . model_location ) ;
2016-04-28 21:40:36 +02:00
cout < < " Model loaded " < < endl ;
2017-10-23 18:58:35 +02:00
// Load facial feature extractor and AU analyser (make sure it is static)
FaceAnalysis : : FaceAnalyserParameters face_analysis_params ( arguments ) ;
face_analysis_params . OptimizeForImages ( ) ;
FaceAnalysis : : FaceAnalyser face_analyser ( face_analysis_params ) ;
2016-06-14 23:55:16 +02:00
2017-11-14 20:59:08 +01:00
// If bounding boxes not provided, use a face detector
cv : : CascadeClassifier classifier ( det_parameters . face_detector_location ) ;
dlib : : frontal_face_detector face_detector_hog = dlib : : get_frontal_face_detector ( ) ;
2016-04-28 21:40:36 +02:00
2017-11-15 20:55:16 +01:00
// A utility for visualizing the results
Utilities : : Visualizer visualizer ( arguments ) ;
2017-11-14 20:59:08 +01:00
cv : : Mat captured_image ;
2016-04-28 21:40:36 +02:00
2017-11-15 21:20:17 +01:00
captured_image = image_reader . GetNextImage ( ) ;
2016-04-28 21:40:36 +02:00
2017-11-14 20:59:08 +01:00
cout < < " Starting tracking " < < endl ;
while ( ! captured_image . empty ( ) )
{
2016-04-28 21:40:36 +02:00
2017-11-14 20:59:08 +01:00
Utilities : : RecorderOpenFaceParameters recording_params ( arguments , false ) ;
Utilities : : RecorderOpenFace open_face_rec ( image_reader . name , recording_params , arguments ) ;
2016-04-28 21:40:36 +02:00
2017-11-16 10:00:47 +01:00
visualizer . SetImage ( captured_image , image_reader . fx , image_reader . fy , image_reader . cx , image_reader . cy ) ;
if ( recording_params . outputGaze ( ) & & ! face_model . eye_model )
cout < < " WARNING: no eye model defined, but outputting gaze " < < endl ;
2017-11-14 20:59:08 +01:00
// Making sure the image is in uchar grayscale
cv : : Mat_ < uchar > grayscale_image = image_reader . GetGrayFrame ( ) ;
2016-04-28 21:40:36 +02:00
2017-11-15 20:57:58 +01:00
// Detect faces in an image
vector < cv : : Rect_ < double > > face_detections ;
if ( image_reader . has_bounding_boxes )
{
face_detections = image_reader . GetBoundingBoxes ( ) ;
}
else
{
if ( det_parameters . curr_face_detector = = LandmarkDetector : : FaceModelParameters : : HOG_SVM_DETECTOR )
2016-04-28 21:40:36 +02:00
{
2017-11-15 20:57:58 +01:00
vector < double > confidences ;
LandmarkDetector : : DetectFacesHOG ( face_detections , grayscale_image , face_detector_hog , confidences ) ;
2016-04-28 21:40:36 +02:00
}
else
{
2017-11-15 20:57:58 +01:00
LandmarkDetector : : DetectFaces ( face_detections , grayscale_image , classifier ) ;
2016-04-28 21:40:36 +02:00
}
2017-11-15 20:57:58 +01:00
}
// Detect landmarks around detected faces
int face_det = 0 ;
// perform landmark detection for every face detected
for ( size_t face = 0 ; face < face_detections . size ( ) ; + + face )
{
// if there are multiple detections go through them
bool success = LandmarkDetector : : DetectLandmarksInImage ( grayscale_image , face_detections [ face ] , face_model , det_parameters ) ;
// Estimate head pose and eye gaze
cv : : Vec6d pose_estimate = LandmarkDetector : : GetPose ( face_model , image_reader . fx , image_reader . fy , image_reader . cx , image_reader . cy ) ;
2016-04-28 21:40:36 +02:00
2017-11-15 20:57:58 +01:00
// Gaze tracking, absolute gaze direction
cv : : Point3f gaze_direction0 ( 0 , 0 , - 1 ) ;
cv : : Point3f gaze_direction1 ( 0 , 0 , - 1 ) ;
cv : : Vec2d gaze_angle ( 0 , 0 ) ;
2017-11-16 10:00:47 +01:00
if ( success & & face_model . eye_model )
2016-04-28 21:40:36 +02:00
{
2017-11-15 20:57:58 +01:00
GazeAnalysis : : EstimateGaze ( face_model , gaze_direction0 , image_reader . fx , image_reader . fy , image_reader . cx , image_reader . cy , true ) ;
GazeAnalysis : : EstimateGaze ( face_model , gaze_direction1 , image_reader . fx , image_reader . fy , image_reader . cx , image_reader . cy , false ) ;
gaze_angle = GazeAnalysis : : GetGazeAngle ( gaze_direction0 , gaze_direction1 ) ;
2016-04-28 21:40:36 +02:00
}
2017-11-15 21:06:23 +01:00
cv : : Mat sim_warped_img ;
cv : : Mat_ < double > hog_descriptor ; int num_hog_rows = 0 , num_hog_cols = 0 ;
// Perform AU detection and HOG feature extraction, as this can be expensive only compute it if needed by output or visualization
if ( recording_params . outputAlignedFaces ( ) | | recording_params . outputHOG ( ) | | recording_params . outputAUs ( ) | | visualizer . vis_align | | visualizer . vis_hog )
{
2017-11-16 18:46:34 +01:00
face_analyser . PredictStaticAUsAndComputeFeatures ( captured_image , face_model . detected_landmarks ) ;
2017-11-15 21:06:23 +01:00
face_analyser . GetLatestAlignedFace ( sim_warped_img ) ;
face_analyser . GetLatestHOG ( hog_descriptor , num_hog_rows , num_hog_cols ) ;
}
2017-11-15 20:57:58 +01:00
// Displaying the tracking visualizations
2017-11-15 21:06:23 +01:00
visualizer . SetObservationFaceAlign ( sim_warped_img ) ;
visualizer . SetObservationHOG ( hog_descriptor , num_hog_rows , num_hog_cols ) ;
2017-11-19 21:04:00 +01:00
visualizer . SetObservationLandmarks ( face_model . detected_landmarks , 1.0 , face_model . detection_success ) ; // Set confidence to high to make sure we always visualize
visualizer . SetObservationPose ( pose_estimate , 1.0 ) ;
2017-11-15 20:57:58 +01:00
visualizer . SetObservationGaze ( gaze_direction0 , gaze_direction1 , LandmarkDetector : : CalculateAllEyeLandmarks ( face_model ) , LandmarkDetector : : Calculate3DEyeLandmarks ( face_model , image_reader . fx , image_reader . fy , image_reader . cx , image_reader . cy ) , face_model . detection_certainty ) ;
// Setting up the recorder output
2017-11-15 21:06:23 +01:00
open_face_rec . SetObservationHOG ( face_model . detection_success , hog_descriptor , num_hog_rows , num_hog_cols , 31 ) ; // The number of channels in HOG is fixed at the moment, as using FHOG
2017-11-15 20:57:58 +01:00
open_face_rec . SetObservationVisualization ( visualizer . GetVisImage ( ) ) ;
open_face_rec . SetObservationActionUnits ( face_analyser . GetCurrentAUsReg ( ) , face_analyser . GetCurrentAUsClass ( ) ) ;
open_face_rec . SetObservationLandmarks ( face_model . detected_landmarks , face_model . GetShape ( image_reader . fx , image_reader . fy , image_reader . cx , image_reader . cy ) ,
face_model . params_global , face_model . params_local , face_model . detection_certainty , face_model . detection_success ) ;
open_face_rec . SetObservationPose ( pose_estimate ) ;
open_face_rec . SetObservationGaze ( gaze_direction0 , gaze_direction1 , gaze_angle , LandmarkDetector : : CalculateAllEyeLandmarks ( face_model ) , LandmarkDetector : : Calculate3DEyeLandmarks ( face_model , image_reader . fx , image_reader . fy , image_reader . cx , image_reader . cy ) ) ;
2017-11-15 21:06:23 +01:00
open_face_rec . SetObservationFaceAlign ( sim_warped_img ) ;
2017-11-15 20:57:58 +01:00
open_face_rec . WriteObservation ( ) ;
}
2017-11-16 10:00:47 +01:00
visualizer . ShowObservation ( ) ;
2017-11-15 20:57:58 +01:00
2017-11-16 18:46:34 +01:00
// Grabbing the next frame in the sequence
captured_image = image_reader . GetNextImage ( ) ;
2016-04-28 21:40:36 +02:00
}
return 0 ;
}