1 #include "gaze/pipeline_steps/gaze_point_calculation.h"
6 #include "dlib/gui_widgets.h"
7 #include "opencv2/opencv.hpp"
8 #include "yaml-cpp/yaml.h"
10 #include "gaze/util/config.h"
11 #include "gaze/util/data.h"
12 #include "gaze/util/dlibcv.h"
13 #include "gaze/util/pipeline_utils.h"
22 int delim = aspect_ratio_string.
find(
":");
24 delim = aspect_ratio_string.
find(
"/");
27 double width_rate = std::stod(aspect_ratio_string.
substr(0, delim));
28 double height_rate = std::stod(aspect_ratio_string.
substr(delim + 1));
29 aspect_ratio = width_rate / height_rate;
31 aspect_ratio = std::stod(aspect_ratio_string);
39 }
else if (value > max) {
49 GazePointCalculation::GazePointCalculation()
50 : eye_ball_centers(2) {
55 this->name = config[
"name"] ?
58 if (config[
"eye_ball_centers"]) {
59 this->eye_ball_centers =
62 this->eye_ball_centers[0] =
cv::Vec3d(-0.029205, 0.0327, -0.0395);
63 this->eye_ball_centers[1] =
cv::Vec3d(0.029205, 0.0327, -0.0395);
66 if (config[
"model"]) {
72 {0.0, -0.0636, -0.0125},
73 {-0.0433, 0.0327, -0.026},
74 {0.0433, 0.0327, -0.026},
75 {-0.0289, -0.0289, -0.0241},
76 {0.0289, -0.0289, -0.0241}
81 YAML::Node camera_config = meta_config[
"camera"];
82 double sensor_aspect_ratio = camera_config[
"sensor_aspect_ratio"] ?
84 camera_config[
"sensor_aspect_ratio"].as<std::string>())
87 this->set_sensor_size(
88 camera_config[
"sensor_size"] ?
89 camera_config[
"sensor_size"].as<double>() : 0.00635,
92 this->camera_matrix = camera_config[
"camera_matrix"].as<
cv::Mat>();
93 this->distortion_coefficients =
94 camera_config[
"distortion_coefficients"].as<
cv::Mat>();
96 this->pixel_width = this->sensor_width /
97 camera_config[
"resolution"][
"width"].as<
double>();
98 this->pixel_height = this->sensor_height /
99 camera_config[
"resolution"][
"height"].as<
double>();
101 this->focal_length = camera_config[
"focal_length"] ?
102 camera_config[
"focal_length"].as<
double>() : 0.01;
104 this->camera_offset_x = camera_config[
"position"][
"x"].as<
double>();
105 this->camera_offset_y = camera_config[
"position"][
"y"].as<
double>();
108 YAML::Node screen_config = meta_config[
"screen"];
109 this->screen_width_m =
110 screen_config[
"measurements"][
"width"].as<
double>();
111 this->screen_height_m =
112 screen_config[
"measurements"][
"height"].as<
double>();
113 this->screen_width_px =
114 screen_config[
"resolution"][
"width"].as<
int>();
115 this->screen_height_px =
116 screen_config[
"resolution"][
"height"].as<
int>();
118 if (meta_config[
"target"]) {
119 this->target_width = meta_config[
"target"][
"width"].as<
int>();
120 this->target_height = meta_config[
"target"][
"height"].as<
int>();
122 this->target_width = this->screen_width_px;
123 this->target_height = this->screen_height_px;
127 double GazePointCalculation::calculate_distance(
128 const dlib::point& p0,
const dlib::point& p1,
129 double expected_model_distance) {
130 double interex_dist_px = dlib::length(p0 - p1);
131 double distance = (this->focal_length * expected_model_distance) /
132 (interex_dist_px * this->pixel_width);
136 cv::Matx34d GazePointCalculation::invertProjection(
141 return transformation;
154 this->camera_matrix, this->distortion_coefficients);
156 for (
const cv::Vec2d& point : restored_points) {
157 cv::Vec3d unprojected(point[0], point[1], 1);
159 rcam * rotation * ((unprojected / distance) - translation));
161 return unprojectedPoints;
164 cv::Vec3d GazePointCalculation::get_model_to_camera_dir(
169 cv::Vec3d GazePointCalculation::get_camera_pos(
170 const cv::Vec3d& model_to_camera_dir,
double distance) {
171 return distance * model_to_camera_dir;
177 cv::Vec3d(-this->camera_offset_x, -this->camera_offset_y, 0);
182 return rotation.
t() * in + camera_pos;
188 cv::Matx32d GazePointCalculation::calculate_gaze_point(
197 cv::Vec3d tuv = (raycast.
inv() * (eye_ball_center - screen_a));
198 cv::Vec3d gaze_point = eye_ball_center + (pupil - eye_ball_center) * tuv[0];
206 void GazePointCalculation::set_sensor_size(
207 double sensor_diagonal,
208 double aspect_ratio) {
209 this->sensor_height =
std::sqrt(sensor_diagonal * sensor_diagonal
210 / (1 + aspect_ratio * aspect_ratio));
211 this->sensor_width = aspect_ratio * this->sensor_height;
221 for (
auto i : this->landmark_indices) {
228 cv::Matx34d transformation = this->invertProjection(landmarks);
233 for (
auto i = decltype(data.
pupils.size()){0};
234 i < data.
pupils.size(); ++i) {
235 dlib::point eye = data.
centers[i] + eyes[i].rect.tl_corner();
240 double distance = this->calculate_distance(
247 cv::Vec3d camera_pos = this->get_camera_pos(model_to_camera_dir, distance);
250 this->get_screen_corners(camera_pos, R);
254 for (
auto i = decltype(data.
gaze_points.size()){0};
256 cv::Matx32d gaze_point = this->calculate_gaze_point(
257 this->eye_ball_centers[i], data.
pupils[i],
258 screen_tl_tr_br_bl[0], screen_tl_tr_br_bl[1], screen_tl_tr_br_bl[3]);
260 cv::Vec3d(gaze_point(0, 0), gaze_point(1, 0), gaze_point(2, 0));
261 screen_coord_coeffs[0] += gaze_point(1, 1);
262 screen_coord_coeffs[1] += gaze_point(1, 2);
264 screen_coord_coeffs /= 2;
266 screen_coord_coeffs[0] = 1 -
util::clamp(screen_coord_coeffs[0], 0, 1);
267 screen_coord_coeffs[1] =
util::clamp(screen_coord_coeffs[1], 0, 1);
270 this->target_width * screen_coord_coeffs[0],
271 this->target_height * screen_coord_coeffs[1]);
276 this->widget->clear_overlay();
282 double distance = this->calculate_distance(
290 for (
auto i = decltype(data.
landmarks.num_parts()){0};
302 cv::Vec3d camera_pos = this->get_camera_pos(camera_dir, distance);
306 this->get_screen_corners(camera_pos, R);
312 auto add_to_overlay =
314 dlib::rgb_pixel color) ->
void {
315 for (
auto p : points) {
316 overlay_dots.
push_back({{p[0], p[1], p[2]}, color});
320 add_to_overlay(this->model, {255, 255, 0});
321 add_to_overlay(data.
pupils, {0, 255, 255});
322 add_to_overlay(this->eye_ball_centers, {255, 0, 255});
323 add_to_overlay(world_landmarks, {0, 255, 0});
328 for (
auto i = decltype(pupils_image.size()){0}; i < pupils_image.size();
330 pupils_image[i] += chips[i].rect.tl_corner();
333 this->unprojectPoints({
cv::Vec2d(pupils_image[0].x(), pupils_image[0].y()),
334 cv::Vec2d(pupils_image[1].x(), pupils_image[1].y())},
335 data.head_translation, R,
distance);
337 add_to_overlay(pupils_world, {255, 255, 255});
340 cv::Vec3d camera = this->get_camera_pos(camera_dir, distance);
341 overlay_dots.push_back({{camera[0], camera[1], camera[2]},
342 dlib::rgb_pixel(255, 255, 255)});
344 this->widget->add_overlay(overlay_dots);
347 dlib::rgb_pixel color) ->
348 dlib::perspective_display::overlay_line {
349 return dlib::perspective_display::overlay_line(
350 {p0[0], p0[1], p0[2]}, {p1[0], p1[1], p1[2]}, color);
359 this->widget->add_overlay({
360 to_line(origin, xdir, {255, 0, 0}),
361 to_line(origin, ydir, {0, 255, 0}),
362 to_line(origin, zdir, {0, 0, 255}),
363 to_line(origin, origin + camera_dir, {255, 255, 0})});
365 this->widget->add_overlay({
366 to_line(screen_corners[0], screen_corners[1], {255, 255, 0}),
367 to_line(screen_corners[1], screen_corners[2], {255, 255, 0}),
368 to_line(screen_corners[2], screen_corners[3], {255, 255, 0}),
369 to_line(screen_corners[3], screen_corners[0], {255, 255, 0})});
371 this->widget->add_overlay({
372 to_line(this->eye_ball_centers[0], data.pupils[0], {0, 255, 255}),
373 to_line(this->eye_ball_centers[1], data.pupils[1], {0, 255, 255})});
375 this->widget->add_overlay({
376 to_line(this->eye_ball_centers[0], data.gaze_points[0], {255, 255, 255}),
377 to_line(this->eye_ball_centers[1], data.gaze_points[1],
void Rodrigues(InputArray src, OutputArray dst, OutputArray jacobian=noArray())
std::vector< dlib::chip_details > get_eyes_chip_details(const dlib::full_object_detection object_detection)
double parse_aspect_ratio(std::string aspect_ratio_string)
void distance(_InputIterator __first, _InputIterator __last, _Distance &__n)
int estimateAffine3D(InputArray src, InputArray dst, OutputArray out, OutputArray inliers, double ransacThreshold=3, double confidence=0.99)
dlib::array< dlib::point > centers
Matx< double, n, m > inv(int method=DECOMP_LU, bool *p_is_ok=NULL) const
std::vector< cv::Vec3d > gaze_points
cv::Vec2d estimated_gaze_point
Matx< double, n, m > t() const
basic_string substr(size_type __pos=0, size_type __n=npos) const
size_type find(const char *__s, size_type __pos, size_type __n) const noexcept
void push_back(const value_type &__x)
dlib::full_object_detection landmarks
static Vec< _Tp, cn > normalize(const Vec< _Tp, cn > &v)
_OutputIterator transform(_InputIterator __first, _InputIterator __last, _OutputIterator __result, _UnaryOperation __unary_op)
double clamp(double value, double min, double max)
void hconcat(const Mat *src, size_t nsrc, OutputArray dst)
Wraps the data acquired per frame into a single instance.
std::vector< cv::Vec3d > pupils
complex< _Tp > sqrt(const complex< _Tp > &)