gaze  0.1.0
Perform gaze tracking with common webcams.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Groups Pages
Public Member Functions | List of all members
gaze::pipeline::PupilLocalization Class Referencefinal

Implements Timm and Barth (2011) Timm2011 to detect eye centers. More...

#include "gaze/pipeline_steps/pupil_localization.h"

Inheritance diagram for gaze::pipeline::PupilLocalization:
gaze::PipelineStep gaze::gui::Visualizeable< widget_type > gaze::gui::VisualizeableBase

Public Member Functions

void process (util::Data &data) override
 
void visualize (util::Data &data) override
 
- Public Member Functions inherited from gaze::PipelineStep
 PipelineStep ()
 
std::string get_name ()
 
virtual ~PipelineStep ()=default
 
- Public Member Functions inherited from gaze::gui::Visualizeable< widget_type >
std::shared_ptr< dlib::drawable > init (dlib::drawable_window &parent, int width, int height, std::string text="No visualization.") final
 
void remove_widget () override
 
- Public Member Functions inherited from gaze::gui::VisualizeableBase
virtual ~VisualizeableBase ()=default
 

Additional Inherited Members

- Protected Attributes inherited from gaze::PipelineStep
int number
 
std::string name
 
- Protected Attributes inherited from gaze::gui::Visualizeable< widget_type >
std::shared_ptr< widget_type > widget
 

Detailed Description

Implements Timm and Barth (2011) Timm2011 to detect eye centers.

This implementation uses a lookup table to pre-calculate the distance function. Additionally, some tweaks are introduced, as explained in the similar implementation eyeLike by Tristan Hume.

For details you can also refer to Tristan's blog post Simple, accurate eye center tracking in OpenCV.

Definition at line 125 of file pupil_localization.h.

Member Function Documentation

void gaze::pipeline::PupilLocalization::process ( util::Data data)
overridevirtual

Detects the eye center for each eye.

Parameters
dataThe data object to be updated.
Returns
via data the modified data object.

Implements gaze::PipelineStep.

Definition at line 88 of file pupil_localization.cpp.

References gaze::util::Data::centers, gaze::util::Data::eyes, gaze::util::fill_displacement_tables(), gaze::util::get_eyes_chip_details(), gaze::util::Data::image, gaze::util::Data::landmarks, std::max(), std::numeric_limits< class >::min(), gaze::util::normalize_and_threshold_gradients(), sum(), and zero().

88  {
89  if (data.landmarks.num_parts() < 5) {
90  return;
91  }
92 
93  // Extract eye patches
95  util::get_eyes_chip_details(data.landmarks);
96  dlib::extract_image_chips(data.image, details, data.eyes);
97 
98  // Resize lookup table if needed
99  int max_size = std::max({data.eyes[0].nr(), data.eyes[0].nc(),
100  data.eyes[1].nr(), data.eyes[1].nc()});
101  if (2 * max_size > this->displacement_table_x.nr()) {
103  this->displacement_table_x,
104  this->displacement_table_y,
105  2 * max_size + 7);
106  }
107 
108  for (int i = 0; i < 2; ++i) {
109  dlib::matrix<double> eye_in;
110  dlib::assign_image(eye_in, data.eyes[i]);
111 
112  // Calculate gradients
113  dlib::matrix<double> eye_horizontal;
114  dlib::matrix<double> eye_vertical;
115  dlib::sobel_edge_detector(eye_in, eye_horizontal, eye_vertical);
116 
117  util::normalize_and_threshold_gradients(eye_horizontal, eye_vertical,
118  this->relative_threshold_factor);
119 
120  // Blur eyes as weights
121  int boundary_offset = 6;
122  dlib::matrix<double> eye_gaussian;
123  dlib::gaussian_blur(eye_in, eye_gaussian, this->sigma, boundary_offset);
124  eye_gaussian = 255 - eye_gaussian;
125 
126  // Compute objective function t and select highest value
127  double max_t = std::numeric_limits<double>::min();
128  int argmax_r;
129  int argmax_c;
130 
131  int center = (this->displacement_table_x.nr() - 1) / 2;
132  auto nr = eye_in.nr();
133  auto nc = eye_in.nc();
134  dlib::matrix<double> zero = dlib::zeros_matrix<double>(nr, nc);
135  dlib::matrix<double> ts = dlib::zeros_matrix<double>(nr, nc);
136  for (auto row = decltype(nr){boundary_offset};
137  row < nr - boundary_offset; ++row) {
138  for (auto col = decltype(nc){boundary_offset};
139  col < nc - boundary_offset; ++col) {
140  dlib::matrix<double> d_x = dlib::subm(this->displacement_table_x,
141  center - row, center - col, nr, nc);
142  dlib::matrix<double> d_y = dlib::subm(this->displacement_table_y,
143  center - row, center - col, nr, nc);
144 
145  double t =
146  dlib::sum(
147  dlib::squared(dlib::max_pointwise(
148  dlib::pointwise_multiply(d_x, eye_horizontal) +
149  dlib::pointwise_multiply(d_y, eye_vertical), zero))) *
150  eye_gaussian(row, col);
151  if (t > max_t) {
152  max_t = t;
153  argmax_r = row;
154  argmax_c = col;
155  }
156  }
157  }
158  data.centers[i] = dlib::point(argmax_c, argmax_r);
159  }
160 }
std::vector< dlib::chip_details > get_eyes_chip_details(const dlib::full_object_detection object_detection)
static softfloat zero()
void fill_displacement_tables(dlib::matrix< double > &table_x, dlib::matrix< double > &table_y, int size)
_GLIBCXX14_CONSTEXPR const _Tp & max(const _Tp &__a, const _Tp &__b)
static constexpr _Tp min() noexcept
void normalize_and_threshold_gradients(dlib::matrix< T > &horizontal, dlib::matrix< T > &vertical, double relative_threshold=-1)
_Tp sum() const
void gaze::pipeline::PupilLocalization::visualize ( util::Data data)
overridevirtual
Parameters
dataThe data object to be updated.

Implements gaze::gui::VisualizeableBase.

Definition at line 162 of file pupil_localization.cpp.

References gaze::util::Data::centers, gaze::util::Data::eyes, gaze::util::get_eyes_chip_details(), gaze::util::Data::image, and gaze::util::Data::landmarks.

162  {
163  if (data.landmarks.num_parts() < 5) {
164  return;
165  }
166  // Ensures output of this pipeline step is visualized
167  this->process(data);
168 
169  // define offsets
170  dlib::point offset_x(5, 0);
171  dlib::point offset_y(0, 5);
173  util::get_eyes_chip_details(data.landmarks);
174 
175  // assign eyes to new images
176  dlib::array<dlib::array2d<dlib::bgr_pixel>> eyes(3);
177  dlib::assign_image(eyes[2], data.image);
178  for (auto i = decltype(data.eyes.size()){0};
179  i < data.eyes.size(); ++i) {
180  dlib::assign_image(eyes[i], data.eyes[i]);
181 
182  dlib::draw_line(eyes[i],
183  data.centers[i] + offset_x,
184  data.centers[i] - offset_x,
185  dlib::rgb_pixel(255, 0, 0));
186  dlib::draw_line(eyes[i],
187  data.centers[i] + offset_y,
188  data.centers[i] - offset_y,
189  dlib::rgb_pixel(255, 0, 0));
190 
191  dlib::draw_line(eyes[2],
192  data.centers[i] + offset_x + chips[i].rect.tl_corner(),
193  data.centers[i] - offset_x + chips[i].rect.tl_corner(),
194  dlib::rgb_pixel(255, 0, 0));
195  dlib::draw_line(eyes[2],
196  data.centers[i] + offset_y + chips[i].rect.tl_corner(),
197  data.centers[i] - offset_y + chips[i].rect.tl_corner(),
198  dlib::rgb_pixel(255, 0, 0));
199  }
200  dlib::resize_image(0.5, eyes[2]);
201 
202  this->widget->set_image(dlib::tile_images(eyes));
203 }
std::vector< dlib::chip_details > get_eyes_chip_details(const dlib::full_object_detection object_detection)
std::shared_ptr< widget_type > widget
void process(util::Data &data) override

The documentation for this class was generated from the following files: