/////////////////////////////////////////////////////////////////////////////// // Copyright (C) 2017, Carnegie Mellon University and University of Cambridge, // all rights reserved. // // ACADEMIC OR NON-PROFIT ORGANIZATION NONCOMMERCIAL RESEARCH USE ONLY // // 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 // * 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š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š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š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š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 // OpenCV includes #include #include #include // Boost includes #include #include #include #include #include #include #include #include #include #include #include #ifndef CONFIG_DIR #define CONFIG_DIR "~" #endif using namespace std; vector get_arguments(int argc, char **argv) { vector arguments; for(int i = 0; i < argc; ++i) { arguments.push_back(string(argv[i])); } return arguments; } // TODO rem 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))); } //LandmarkDetector::Draw(display_image, clnf_model); } int main (int argc, char **argv) { //Convert arguments to more convenient vector form vector arguments = get_arguments(argc, argv); // Prepare for image reading Utilities::ImageCapture image_reader; // The sequence reader chooses what to open based on command line arguments provided if (!image_reader.Open(arguments)) { cout << "Could not open any images" << endl; return 1; } // Load the models if images found LandmarkDetector::FaceModelParameters det_parameters(arguments); // No need to validate detections, as we're not doing tracking det_parameters.validate_detections = false; // The modules that are being used for tracking cout << "Loading the model" << endl; LandmarkDetector::CLNF face_model(det_parameters.model_location); cout << "Model loaded" << endl; // 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); // 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(); // A utility for visualizing the results Utilities::Visualizer visualizer(arguments); cv::Mat captured_image; captured_image = image_reader.GetNextImage(); cout << "Starting tracking" << endl; while (!captured_image.empty()) { Utilities::RecorderOpenFaceParameters recording_params(arguments, false); Utilities::RecorderOpenFace open_face_rec(image_reader.name, recording_params, arguments); // Making sure the image is in uchar grayscale cv::Mat_ grayscale_image = image_reader.GetGrayFrame(); // Detect faces in an image vector > 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) { vector confidences; LandmarkDetector::DetectFacesHOG(face_detections, grayscale_image, face_detector_hog, confidences); } else { LandmarkDetector::DetectFaces(face_detections, grayscale_image, classifier); } } // 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); // 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); if (success && det_parameters.track_gaze) { 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); } cv::Mat sim_warped_img; cv::Mat_ 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) { face_analyser.PredictStaticAUs(captured_image, face_model.detected_landmarks); face_analyser.GetLatestAlignedFace(sim_warped_img); face_analyser.GetLatestHOG(hog_descriptor, num_hog_rows, num_hog_cols); } // Displaying the tracking visualizations visualizer.SetImage(captured_image, image_reader.fx, image_reader.fy, image_reader.cx, image_reader.cy); visualizer.SetObservationFaceAlign(sim_warped_img); visualizer.SetObservationHOG(hog_descriptor, num_hog_rows, num_hog_cols); visualizer.SetObservationLandmarks(face_model.detected_landmarks, face_model.detection_certainty, face_model.detection_success); visualizer.SetObservationPose(pose_estimate, face_model.detection_certainty); 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); visualizer.ShowObservation(); // Setting up the recorder output 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 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)); open_face_rec.SetObservationFaceAlign(sim_warped_img); open_face_rec.WriteObservation(); // Grabbing the next frame in the sequence captured_image = image_reader.GetNextImage(); } } return 0; }