@@ -23,16 +23,26 @@ PositionSolver::PositionSolver(
2323 this ->prior_yaw = -1.57 ;
2424 this ->prior_distance = prior_distance * -2.0 ;
2525
26+ this ->width = width;
27+ this ->height = height;
28+
2629 this ->rv [0 ] = this ->prior_pitch ;
2730 this ->rv [1 ] = this ->prior_yaw ;
2831 this ->rv [2 ] = -1.57 ;
2932 this ->tv [2 ] = this ->prior_distance ;
3033
34+ // head3dScale = (cv::Mat_<double>(3, 3) <<
35+ // y_scale, 0.0, 0, // pitch is rv[0], pitch involves y-axis
36+ // 0.0, x_scale, 0, // yaw is rv[1], yaw involves x-axis
37+ // 0.0, 0.0, z_scale
38+ // );
39+
40+
3141 head3dScale = (cv::Mat_<double >(3 , 3 ) <<
3242 y_scale, 0.0 , 0 , // pitch is rv[0], pitch involves y-axis
3343 0.0 , x_scale, 0 , // yaw is rv[1], yaw involves x-axis
3444 0.0 , 0.0 , z_scale
35- );
45+ );
3646
3747 this ->complex = complex ;
3848
@@ -67,9 +77,9 @@ PositionSolver::PositionSolver(
6777 landmark_points_buffer = cv::Mat ((int )contour_indices.size (), 1 , CV_32FC2);
6878
6979 mat3dcontour = (cv::Mat_<double >((int )contour_indices.size (), 3 ) <<
70- 0.45517698 , -0.30089578 , 0.76442945 ,
71- 0.44899884 , -0.16699584 , 0.76514298 ,
72- 0.43743154 , -0.02265548 , 0.73926717 ,
80+ 0.45517698 , -0.30089578 , 0.76442945 , // 0
81+ 0.44899884 , -0.16699584 , 0.76514298 , // 1
82+ 0.43743154 , -0.02265548 , 0.73926717 ,
7383 0.41503343 , 0.08894145 , 0.74794745 ,
7484 0.38912359 , 0.23238003 , 0.70478839 ,
7585 0.3346301 , 0.36126539 , 0.61558759 ,
@@ -159,7 +169,11 @@ PositionSolver::PositionSolver(
159169
160170 camera_distortion = (cv::Mat_<double >(4 , 1 ) << 0 , 0 , 0 , 0 );
161171
162- mat3dcontour = mat3dcontour * head3dScale;
172+ // mat3dcontour = mat3dcontour * head3dScale;
173+
174+ cv::transpose (mat3dcontour, mat3dcontour);
175+ mat3dcontour = head3dScale * mat3dcontour;
176+ cv::transpose (mat3dcontour, mat3dcontour);
163177
164178 if (complex ) std::cout << " Using complex solver" << std::endl;
165179}
@@ -173,7 +187,7 @@ void PositionSolver::solve_rotation(FaceData* face_data)
173187 for (int i = 0 ; i < contour_indices.size (); i++)
174188 {
175189 contour_idx = contour_indices[i];
176- landmark_points_buffer.at <float >(i, j) = (float )( int ) face_data->landmark_coords [2 * contour_idx + j]; // fix complation warnings.
190+ landmark_points_buffer.at <float >(i, j) = (float )face_data->landmark_coords [2 * contour_idx + j]; // fix complation warnings.
177191 }
178192 }
179193
@@ -190,7 +204,6 @@ void PositionSolver::solve_rotation(FaceData* face_data)
190204 cv::SOLVEPNP_ITERATIVE
191205 );
192206
193-
194207 get_euler (rvec, tvec);
195208
196209
@@ -200,16 +213,13 @@ void PositionSolver::solve_rotation(FaceData* face_data)
200213 face_data->translation [i] = tvec.at <double >(i, 0 ) * 10 ; // scale solvePnP coordinates to opentrack units in centimeters
201214 }
202215
203- // We dont want the Z axis oversaturated since opentrack has +/-600 centimeter range
204- face_data-> translation [ 2 ] /= 100 ;
216+ correct_rotation (*face_data);
217+ clip_rotations (* face_data) ;
205218
206219#ifdef _DEBUG
207220 std::cout << face_data->to_string () << std::endl; // disable copy constructor and output to std::cout
208221#endif
209222
210- correct_rotation (*face_data);
211- clip_rotations (*face_data);
212-
213223}
214224
215225void PositionSolver::set_prior_pitch (float new_pitch)
@@ -230,6 +240,45 @@ void PositionSolver::set_prior_distance(float new_distance)
230240 this ->tv [2 ] = this ->prior_distance ;
231241}
232242
243+ void PositionSolver::calibrate_head_scale (FaceData& face_data)
244+ {
245+ std::tuple<double , double > face_dims = get_3dhead_dims ();
246+ double width = std::get<0 >(face_dims);
247+ double height = std::get<1 >(face_dims);
248+
249+ double real_ratio = width / height;
250+
251+ std::tuple<double , double > model_dims = get_3dhead_dims ();
252+ double model_width = std::get<0 >(model_dims);
253+ double model_height = std::get<1 >(model_dims);
254+
255+ double model_ratio = model_width / model_height;
256+
257+ double scale = (model_height * real_ratio) / height;
258+
259+ head3dScale.at <double >(0 , 0 ) = scale;
260+ }
261+
262+ double PositionSolver::get_x_scale ()
263+ {
264+ return head3dScale.at <double >(1 , 1 );
265+ }
266+
267+ std::tuple<double , double > PositionSolver::get_3dhead_dims ()
268+ {
269+ // indices of the matrix rows, not actual points!
270+ double model_width = abs (mat3dcontour.at <double >(0 , 0 ) - mat3dcontour.at <double >(8 , 0 )); // 0 - 16
271+ double model_height = abs (mat3dcontour.at <double >(9 , 1 ) - mat3dcontour.at <double >(4 , 1 )); // 27 -8
272+ return std::tuple<double , double >(model_width, model_height);
273+ }
274+
275+ std::tuple<double , double > PositionSolver::get_2dhead_dims (FaceData& face_data)
276+ {
277+ double width = abs (face_data.landmark_coords [0 + 1 ] - face_data.landmark_coords [16 * 2 + 1 ]);
278+ double height = abs (face_data.landmark_coords [27 * 2 + 0 ] - face_data.landmark_coords [8 * 2 + 0 ]);
279+ return std::tuple<double , double >(width, height);
280+ }
281+
233282
234283void PositionSolver::get_euler (cv::Mat& rvec, cv::Mat& tvec)
235284{
@@ -254,19 +303,32 @@ void PositionSolver::get_euler(cv::Mat& rvec, cv::Mat& tvec)
254303
255304void PositionSolver::correct_rotation (FaceData& face_data)
256305{
257- float distance = (float ) - (face_data.translation [2 ]);
306+ float distance = (float ) abs (face_data.translation [2 ]);
258307 float lateral_offset = (float )face_data.translation [1 ];
259308 float verical_offset = (float )face_data.translation [0 ];
260309
261- float correction_yaw = (float )(std::atan (lateral_offset / distance) * TO_DEG); // (lateral_offset / distance) is already tangent, so only need atan to obtain radians
262- float correction_pitch = (float )(std::atan (verical_offset / distance) * TO_DEG); // (verical_offset / distance) is already tangent, so only need atan to obtain radians
310+ // float correction_yaw = (float)std::atan((distance / abs(lateral_offset))) * TO_DEG;
311+ // float correction_pitch = (float)(distance / std::atan(verical_offset) * TO_DEG);
312+
313+
314+ float correction_yaw = 90 .0f - (float )std::atan2 (distance, abs (lateral_offset)) * TO_DEG;
315+ float correction_pitch = 90 .0f - (float )std::atan2 (distance, abs (verical_offset)) * TO_DEG;
316+
317+ if (lateral_offset < 0 )
318+ correction_yaw *= -1 ;
319+
320+ if (verical_offset < 0 )
321+ correction_pitch *= -1 ;
263322
264323 face_data.rotation [1 ] += correction_yaw;
265324 face_data.rotation [0 ] += correction_pitch;
266325
267326 // Note: We could saturate pitch here, but its better to let the user do it via Opentrack.
268327 // The coefficient could be problematic for some users.
269328 // face_data.rotation[0] = face_data.rotation[0] * 1.5;
329+
330+ // We dont want the Z axis oversaturated since opentrack has +/-600 centimeter range
331+ face_data.translation [2 ] /= 10 ;
270332}
271333
272334
@@ -282,10 +344,89 @@ void PositionSolver::clip_rotations(FaceData& face_data)
282344 face_data.rotation [0 ] = 90.0 ;
283345 else if (face_data.rotation [0 ] <= -90.0 )
284346 face_data.rotation [0 ] = -90.0 ;
285- // Limit roll between -90.0 and +90.0
286- if (face_data.rotation [2 ] >= 90.0 )
287- face_data.rotation [2 ] = 90.0 ;
288- else if (face_data.rotation [2 ] <= -90.0 )
289- face_data.rotation [2 ] = -90.0 ;
347+ // Limit roll between 0.0 and +180.0
348+ if (face_data.rotation [2 ] >= 180.0 )
349+ face_data.rotation [2 ] = 180.0 ;
350+ else if (face_data.rotation [2 ] <= 0.0 )
351+ face_data.rotation [2 ] = 0.0 ;
352+ }
353+
354+
355+
356+ /*
357+ * SIMPLE POSITION SOLVER
358+ */
359+ SimplePositionSolver::SimplePositionSolver (int im_width, int im_height, float prior_pitch, float prior_yaw, float prior_distance, bool complex , float fov, float x_scale, float y_scale, float z_scale):
360+ PositionSolver(im_width, im_height,prior_pitch, prior_yaw, prior_distance, complex , fov, x_scale, y_scale, z_scale)
361+ {
362+ contour_indices = { 0 ,1 ,2 ,3 ,8 ,13 ,14 ,15 ,16 ,27 ,28 ,29 ,30 ,39 ,42 ,57 }; // 57 == 55 in the 3d model
363+
364+ landmark_points_buffer = cv::Mat ((int )contour_indices.size (), 1 , CV_32FC2);
365+
366+ mat3dcontour = (cv::Mat_<double >((int )contour_indices.size (), 3 ) <<
367+ 0.4551769692672 , 0.300895790030204 , -0.764429433974752 ,
368+ 0.448998827123556 , 0.166995837790733 , -0.765143004071253 ,
369+ 0.437431554952677 , 0.022655479179981 , -0.739267175112735 ,
370+ 0.415033422928434 , -0.088941454648772 , -0.747947437846473 ,
371+ 0 ., -0.621079019321682 , -0.287294770748887 ,
372+ -0.415033422928434 , -0.088941454648772 , -0.747947437846473 ,
373+ -0.437431554952677 , 0.022655479179981 , -0.739267175112735 ,
374+ -0.448998827123556 , 0.166995837790733 , -0.765143004071253 ,
375+ -0.4551769692672 , 0.300895790030204 , -0.764429433974752 ,
376+ 0 ., 0.293332603215811 , -0.137582088779393 ,
377+ 0 ., 0.194828701837823 , -0.069158109325951 ,
378+ 0 ., 0.103844017393155 , -0.009151819844964 ,
379+ 0 ., 0 ., 0 .,
380+ 0.131229723798772 , 0.284447361805627 , -0.234239149487417 ,
381+ -0.131229723798772 , 0.284447361805627 , -0.234239149487417 ,
382+ 0 ., -0.343742581679188 , -0.113925986025684
383+ );
384+
385+ // This 3d model is "inverted", so we need to also invert scales
386+ head3dScale = (cv::Mat_<double >(3 , 3 ) <<
387+ y_scale, 0.0 , 0 , // pitch is rv[0], pitch involves y-axis
388+ 0.0 , x_scale, 0 , // yaw is rv[1], yaw involves x-axis
389+ 0.0 , 0.0 , z_scale
390+ );
391+
392+ cv::transpose (mat3dcontour, mat3dcontour);
393+ mat3dcontour = head3dScale * mat3dcontour;
394+ cv::transpose (mat3dcontour, mat3dcontour);
395+ }
396+
397+
398+
399+ std::tuple<double , double > SimplePositionSolver::get_3dhead_dims ()
400+ {
401+ // indices of the matrix rows, not actual points!
402+ double model_width = abs (mat3dcontour.at <double >(0 , 0 ) - mat3dcontour.at <double >(8 , 0 )); // 0 - 16
403+ double model_height = abs (mat3dcontour.at <double >(9 , 1 ) - mat3dcontour.at <double >(4 , 1 )); // 27 -8
404+ return std::tuple<double , double >(model_width, model_height);
405+ }
406+
407+ std::tuple<double , double > SimplePositionSolver::get_2dhead_dims (FaceData& face_data)
408+ {
409+ double width = abs (face_data.landmark_coords [0 + 1 ] - face_data.landmark_coords [16 * 2 + 1 ]);
410+ double height = abs (face_data.landmark_coords [27 * 2 + 0 ] - face_data.landmark_coords [8 * 2 + 0 ]);
411+ return std::tuple<double , double >(width, height);
290412}
291413
414+
415+ void SimplePositionSolver::correct_rotation (FaceData& face_data)
416+ {
417+ // For some reason the solver gets rest "Pitch" as "-180 deg", which is the same
418+ // as "0 deg", which is what the other solvers find.
419+ // For the moment this method will be overriden beacuse it's very possible that
420+ // this simpler model will change (soon).
421+
422+ if (abs (face_data.rotation [0 ]) > 90 )
423+ if (face_data.rotation [0 ] >= -180 && face_data.rotation [0 ] <= 0 ) {
424+ face_data.rotation [0 ] += 180 ;
425+ }
426+ else if (face_data.rotation [0 ] > 0 && face_data.rotation [0 ] <= 180 )
427+ {
428+ face_data.rotation [0 ] -= 180 ;
429+ }
430+
431+ PositionSolver::correct_rotation (face_data);
432+ }
0 commit comments