From 3a7d81009b89939c88fbdcadff91e8e597cad363 Mon Sep 17 00:00:00 2001 From: Vikram Voleti Date: Fri, 3 Nov 2017 23:21:32 +0530 Subject: [PATCH] Added ability to run on multiple files at once Made same file for single images and multiple images in a .txt file --- CMakeLists.txt | 4 +- README.md | 15 +- ...mark_head_pose_estimation_single_frame.cpp | 82 ---------- .../estimate_head_pose_from_image_or_file.cpp | 153 ++++++++++++++++++ tools/ex_images.txt | 3 + 5 files changed, 172 insertions(+), 85 deletions(-) delete mode 100644 tools/benchmark_head_pose_estimation_single_frame.cpp create mode 100644 tools/estimate_head_pose_from_image_or_file.cpp create mode 100644 tools/ex_images.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 897f94b..d20d4d0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -101,8 +101,8 @@ if(WITH_TOOLS) find_package(Boost COMPONENTS program_options REQUIRED) - add_executable(gazr_benchmark_head_pose_single_frame tools/benchmark_head_pose_estimation_single_frame.cpp) - target_link_libraries(gazr_benchmark_head_pose_single_frame gazr ${OpenCV_LIBRARIES}) + add_executable(gazr_estimate_head_pose tools/estimate_head_pose_from_image_or_file.cpp) + target_link_libraries(gazr_estimate_head_pose gazr ${OpenCV_LIBRARIES}) add_executable(gazr_estimate_head_direction tools/estimate_head_direction.cpp) target_link_libraries(gazr_estimate_head_direction gazr ${OpenCV_LIBRARIES} ${Boost_LIBRARIES}) diff --git a/README.md b/README.md index e6df448..15daac4 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ apt-get install libdlib-dev` You also need OpenCV. On Ubuntu, `sudo apt-get install libopencv-dev`. -### Installation +### Installation - 1/2 The library uses a standard ``CMake`` workflow: @@ -64,9 +64,22 @@ $ cmake .. $ make ``` +#### Example - show head pose + Run ``./gazr_show_head_pose ../share/shape_predictor_68_face_landmarks.dat`` to test the library. You should get something very similar to the picture above. +#### Example - estimate head pose on image/images + +Run ``./gazr_estimate_head_pose ../share/shape_predictor_68_face_landmarks.dat frame.jpg`` +to print the head pose detected in _frame.jpg_. + +Run ``./gazr_estimate_head_pose ../share/shape_predictor_68_face_landmarks.dat ex_images.txt`` +to print the head pose detected in each image file listed in _filenames.txt_ (image file names written in new lines). + + +### Installation - 2/2 + Finally, to install the library: ``` diff --git a/tools/benchmark_head_pose_estimation_single_frame.cpp b/tools/benchmark_head_pose_estimation_single_frame.cpp deleted file mode 100644 index 82c4249..0000000 --- a/tools/benchmark_head_pose_estimation_single_frame.cpp +++ /dev/null @@ -1,82 +0,0 @@ -#define STR_EXPAND(tok) #tok -#define STR(tok) STR_EXPAND(tok) - -#ifdef OPENCV3 -#include -#else -#include -#endif - -#include - -#include "../src/head_pose_estimation.hpp" - -using namespace std; -using namespace cv; - -const static size_t NB_TESTS = 100; // number of time the detection is run, to get better average detection duration - -int main(int argc, char **argv) -{ - Mat frame; - - if(argc < 3) { - cerr << argv[0] << " " << STR(GAZR_VERSION) << "\n\nUsage: " - << endl << argv[0] << " model.dat frame.{jpg|png}" << endl; -#ifdef HEAD_POSE_ESTIMATION_DEBUG - cerr << "Output: a new frame 'head_pose_.png'" << endl; -#endif - return 1; - } - - - auto estimator = HeadPoseEstimation(argv[1]); - - cout << "Estimating head pose on " << argv[2] << endl; -#ifdef OPENCV3 - Mat img = imread(argv[2],IMREAD_COLOR); -#else - Mat img = imread(argv[2],CV_LOAD_IMAGE_COLOR); -#endif - - estimator.focalLength = 500; - - - cout << "Running " << NB_TESTS << " loops to get a good performance estimate..." << endl; -#ifdef HEAD_POSE_ESTIMATION_DEBUG - cerr << "ATTENTION! The benchmark is compiled in DEBUG mode: the performance is no going to be good!!" << endl; -#endif - auto t_start = getTickCount(); - - auto nbfaces = 0; - - for(size_t i = 0; i < NB_TESTS; i++) { - estimator.update(img); - } - auto t_detection = getTickCount(); - - for(size_t i = 0; i < NB_TESTS; i++) { - auto poses = estimator.poses(); - nbfaces += poses.size(); // this is completly artifical: the only purpose is to make sure the compiler does not optimize away estimator.poses() - } - auto t_end = getTickCount(); - - cout << "Found " << nbfaces/NB_TESTS << " faces" << endl; - - auto poses = estimator.poses(); - for(auto pose : poses) { - cout << "Head pose: (" << pose(0,3) << ", " << pose(1,3) << ", " << pose(2,3) << ")" << endl; - - } - - cout << "Face feature detection: " <<((t_detection-t_start) / NB_TESTS) /getTickFrequency() * 1000. << "ms;"; - cout << "Pose estimation: " <<((t_end-t_detection) / NB_TESTS) /getTickFrequency() * 1000. << "ms;"; - cout << "Total time: " << ((t_end-t_start) / NB_TESTS) / getTickFrequency() * 1000. << "ms" << endl; - -#ifdef HEAD_POSE_ESTIMATION_DEBUG - imwrite("head_pose.png", estimator._debug); -#endif - -} - - diff --git a/tools/estimate_head_pose_from_image_or_file.cpp b/tools/estimate_head_pose_from_image_or_file.cpp new file mode 100644 index 0000000..bd62dd5 --- /dev/null +++ b/tools/estimate_head_pose_from_image_or_file.cpp @@ -0,0 +1,153 @@ +#define STR_EXPAND(tok) #tok +#define STR(tok) STR_EXPAND(tok) + +#ifdef OPENCV3 +#include +#else +#include +#endif + +#include + +#include "../src/head_pose_estimation.hpp" + +using namespace std; +using namespace cv; + +const static size_t NB_TESTS = 100; // number of time the detection is run, to get better average detection duration + +std::vector readFileToVector(const std::string& filename) +{ + std::ifstream source; + source.open(filename); + std::vector lines; + std::string line; + while (std::getline(source, line)) + { + lines.push_back(line); + } + return lines; +} + + +void estimate_head_pose_on_frameFileName(const std::string& frameFileName, HeadPoseEstimation estimator, std::vector& prev_poses, bool print_prev_poses) +{ + cout << "Estimating head pose on " << frameFileName << endl; +#ifdef OPENCV3 + Mat img = imread(frameFileName, IMREAD_COLOR); +#else + Mat img = imread(frameFileName, CV_LOAD_IMAGE_COLOR); +#endif + + auto nbfaces = 0; + + for(size_t i = 0; i < NB_TESTS; i++) { + estimator.update(img); + } + + // auto t_detection = getTickCount(); + + for(size_t i = 0; i < NB_TESTS; i++) { + auto poses = estimator.poses(); + nbfaces += poses.size(); // this is completly artifical: the only purpose is to make sure the compiler does not optimize away estimator.poses() + } + + cout << "Found " << nbfaces/NB_TESTS << " face(s)" << endl; + + auto poses = estimator.poses(); + if (poses.size() > 0) { + for(auto pose : poses) { + cout << "Head pose: (" << pose(0,3) << ", " << pose(1,3) << ", " << pose(2,3) << ")" << endl; + } + prev_poses = poses; + } + else if (print_prev_poses) { + for(auto pose : prev_poses) { + cout << "Head pose: (" << pose(0,3) << ", " << pose(1,3) << ", " << pose(2,3) << ")" << endl; + } + } + else { + cout << "Head pose not calculated!" << endl; + } + +} + + +int main(int argc, char **argv) +{ + Mat frame; + + if(argc < 3) { + cerr << argv[0] << " " << STR(GAZR_VERSION) << "\n\nUsage: " + << endl << argv[0] << " model.dat frame.{jpg|png}\n\nOR\n\n" + << endl << argv[0] << " model.dat filenames.txt" << endl; +#ifdef HEAD_POSE_ESTIMATION_DEBUG + cerr << "Output: a new frame 'head_pose_.png'" << endl; +#endif + return 1; + } + + std::string fileName = argv[2]; + + if (fileName.find(".jpg") == std::string::npos and + fileName.find(".png") == std::string::npos and + fileName.find(".txt") == std::string::npos) { + cerr << "Please input a .jpg file, or .png file, or .txt file containing list of images in individual lines."; + return 1; + } + + auto estimator = HeadPoseEstimation(argv[1]); + estimator.focalLength = 500; + + cout << "Running " << NB_TESTS << " loops to get a good performance estimate..." << endl; +#ifdef HEAD_POSE_ESTIMATION_DEBUG + cerr << "ATTENTION! The benchmark is compiled in DEBUG mode: the performance is no going to be good!!" << endl; +#endif + + // Prev pose (default) + head_pose prev_pose = { + -1, -1, -1, -1., + -1, -1, -1, -1., + -1, -1, -1, -1., + 0, 0, 0, 1 + }; + std::vector prev_poses; + prev_poses.push_back(prev_pose); + + auto t_start = getTickCount(); + + // Single image file + if (fileName.find(".jpg") != std::string::npos or fileName.find(".png") != std::string::npos) { + cout << ".jpg or .png file" << endl; + estimate_head_pose_on_frameFileName(fileName, estimator, prev_poses, false); + } + + // Multiple lines + else { + cout << ".txt file" << endl; + // Read file + std::string frameFilesTxt(fileName); + std::vector frameFileNames = readFileToVector(frameFilesTxt); + + if (frameFileNames.size() == 0) { + cout << fileName << "does not exist, or has no image names" << endl; + } + + for(auto frameFileName: frameFileNames) { + if (frameFileName.find("jpg") != std::string::npos or frameFileName.find("png") != std::string::npos) { + estimate_head_pose_on_frameFileName(frameFileName, estimator, prev_poses, false); + } + } + } + + auto t_end = getTickCount(); + + // cout << "Face feature detection: " <<((t_detection-t_start) / NB_TESTS) /getTickFrequency() * 1000. << "ms;"; + // cout << "Pose estimation: " <<((t_end-t_detection) / NB_TESTS) /getTickFrequency() * 1000. << "ms;"; + cout << "Total time: " << (t_end-t_start) / getTickFrequency() * 1000. << "ms" << endl; + +#ifdef HEAD_POSE_ESTIMATION_DEBUG + imwrite("head_pose.png", estimator._debug); +#endif + +} diff --git a/tools/ex_images.txt b/tools/ex_images.txt new file mode 100644 index 0000000..67156c0 --- /dev/null +++ b/tools/ex_images.txt @@ -0,0 +1,3 @@ +/home/voletiv/AMOUNT_00745_19.jpg +/home/voletiv/AMOUNT_00745_20.jpg +/home/voletiv/AMOUNT_00745_21.jpg