FaceGen 3 SDKs Reference
Loading...
Searching...
No Matches
bootPoints.cpp
1//
2// Copyright (c) Singular Inversions Inc. 1999.
3//
4// File: bootPoints.cpp
5// Description: Fg3InitPoints Class Definitions
6// Authors: Andrew Beatty
7// Created: Dec 8, 1999
8//
9
10#include "stdafx.h"
11
12#include "bootPoints.hpp"
13#include "FgSerial.hpp"
14#include "FgFile.hpp"
15
16using namespace std;
17
18namespace Fg {
19
20string const Fg3InitPoints::labels[] =
21{
22 "BROW_CENTRE",
23 "BROW_LEFT",
24 "BROW_RIGHT",
25 "EYE_LEFT_INNER", // endocanthions and ectocanthions.
26 "EYE_LEFT_OUTER",
27 "EYE_RIGHT_INNER",
28 "EYE_RIGHT_OUTER",
29 "CHEEKBONE_LEFT", // occluding contour cheek points.
30 "CHEEKBONE_RIGHT",
31 "NARE_LEFT",
32 "NARE_RIGHT",
33 "NOSE_BASE",
34 "NOSE_TIP",
35 "MOUTH_LEFT",
36 "MOUTH_RIGHT",
37 "CHIN", // tip of chin.
38 "THROAT_TOP",
39 "EYE_LEFT_CENTRE",
40 "EYE_RIGHT_CENTRE",
41 "EYE_LID_UPPER_LEFT", // Horizontal centre of lid edge.
42 "EYE_LID_UPPER_RIGHT",
43 "EYE_LID_LOWER_LEFT",
44 "EYE_LID_LOWER_RIGHT",
45 "JAW_OUTER_LEFT", // Just below bottom of ears.
46 "JAW_OUTER_RIGHT",
47 "CHIN_LOWER", // Just underneath chin.
48 "NOSE_BRIDGE",
49 "SELLION",
50 "LIP_TIP_LOWER",
51 "LIP_TIP_UPPER",
52 "LIP_CHIN_CONCAVITY"
53};
54
55Fg3InitPoints::Fg3InitPoints()
56{
57 for (uint ii=0; ii<LM3_SIZE; ii++) {
58 m_valid[ii] = false;
59 m_pos[ii] = FutVect2IC(0);
60 }
61}
62
63// Sets the boot points from unordered points. These points are expected
64// to be in FR2 raster coordinate system (RCS - origin at centre of top left
65// pixel, x right, y down, one unit per SL0 pixel) with the face imaged
66// upright.
67//
68// There must be 15 points for a frontal view, 9 points for an angled
69// view and 6 points for a profile view.
70//
71inline bool opX(const FutVect2IC a,const FutVect2IC b)
72{
73 return (a.x1 < b.x1);
74}
75inline bool opY(const FutVect2IC a,const FutVect2IC b)
76{
77 return (a.x2 < b.x2);
78}
79
80bool Fg3InitPoints::set(vector<FutVect2IC> pts)
81{
82 static_assert(LM3_SIZE == 31,"This algorithm is specific to the particular layout of bpts");
83 for (uint ii=0; ii<LM3_SIZE; ii++)
84 m_valid[ii] = false;
85 size_t num = pts.size();
86 // The different number of landmark points tells us which viewpoint
87 // and thus how the points should be parsed. All but the current
88 // have been commented out.
89 //
90// else if (num == 14)
91// return setVFrontal(pts);
92// else if (num == 13)
93// return setFrontal(pts);
94 if (num == 11)
95 return setV31Frontal(pts);
96 else if (num == 9)
97 return setV31Profile(pts);
98// else if (num == 9)
99// return setVProfile(pts);
100// else if (num == 8)
101// return setProfile(pts);
102 else if (num == 6)
103 return setPsProfile(pts);
104 else if (num == 5)
105 return setPsFrontal(pts);
106 return false;
107}
108
109// Sets low and high boundaries for a crop region that makes the face
110// fill the image with a suitable boundary. The results are given in
111// RCS.
112//
113void Fg3InitPoints::getCropVals(
114 FutVect2FC &lo,
115 FutVect2FC &hi)
116{
117 // First, find the bounding box of the bootstrap points.
118 bool anyValid = false;
119 lo.x1 = hi.x1 = static_cast<float>(m_pos[LM3_NOSE_TIP].x1);
120 lo.x2 = hi.x2 = static_cast<float>(m_pos[LM3_NOSE_TIP].x2);
121 for (uint ii=0; ii<LM3_SIZE; ii++)
122 {
123 if (m_valid[ii])
124 {
125 if (!anyValid)
126 {
127 lo.x1 = hi.x1 = static_cast<float>(m_pos[ii].x1);
128 lo.x2 = hi.x2 = static_cast<float>(m_pos[ii].x2);
129 anyValid = true;
130 }
131 else
132 {
133 if (static_cast<float>(m_pos[ii].x1) < lo.x1)
134 lo.x1 = static_cast<float>(m_pos[ii].x1);
135 if (static_cast<float>(m_pos[ii].x1) > hi.x1)
136 hi.x1 = static_cast<float>(m_pos[ii].x1);
137 if (static_cast<float>(m_pos[ii].x2) < lo.x2)
138 lo.x2 = static_cast<float>(m_pos[ii].x2);
139 if (static_cast<float>(m_pos[ii].x2) > hi.x2)
140 hi.x2 = static_cast<float>(m_pos[ii].x2);
141 }
142 }
143 }
144 FGASSERT(anyValid);
145
146 // Now guess at the best crop box depending on the viewpoint
147 // implied by the bootstrap points.
148 FutVect2FC centre = (lo + hi) / 2.0f;
149 float size = hi.x2 - lo.x2;
150
151 if (m_angle == VA3_VFRONTAL)
152 {
153 lo.x1 = centre.x1 - size * 0.8f;
154 hi.x1 = centre.x1 + size * 0.8f;
155 lo.x2 = centre.x2 - size * 0.95f;
156 hi.x2 = centre.x2 + size * 0.65f;
157 }
158 else if (m_angle == VA3_FRONTAL)
159 {
160 lo.x1 = centre.x1 - size * 0.8f;
161 hi.x1 = centre.x1 + size * 0.8f;
162 lo.x2 = centre.x2 - size * 0.9f;
163 hi.x2 = centre.x2 + size * 0.7f;
164 }
165 else if (m_angle == VA3_UPWARD)
166 {
167 lo.x1 = centre.x1 - size * 1.15f;
168 hi.x1 = centre.x1 + size * 1.15f;
169 lo.x2 = centre.x2 - size * 1.1f;
170 hi.x2 = centre.x2 + size * 1.2f;
171 }
172 else if (m_angle == VA3_THIRTY_RIGHT)
173 {
174 lo.x1 = centre.x1 - size * 1.0f;
175 hi.x1 = centre.x1 + size * 0.8f;
176 lo.x2 = centre.x2 - size * 1.0f;
177 hi.x2 = centre.x2 + size * 0.8f;
178 }
179 else if (m_angle == VA3_THIRTY_LEFT)
180 {
181 lo.x1 = centre.x1 - size * 0.8f;
182 hi.x1 = centre.x1 + size * 1.0f;
183 lo.x2 = centre.x2 - size * 1.0f;
184 hi.x2 = centre.x2 + size * 0.8f;
185 }
186 else if (m_angle == VA3_VPROFILE_RIGHT)
187 {
188 lo.x1 = centre.x1 - size * 0.83f;
189 hi.x1 = centre.x1 + size * 0.53f;
190 lo.x2 = centre.x2 - size * 0.76f;
191 hi.x2 = centre.x2 + size * 0.60f;
192 }
193 else if (m_angle == VA3_VPROFILE_LEFT)
194 {
195 lo.x1 = centre.x1 - size * 0.53f;
196 hi.x1 = centre.x1 + size * 0.83f;
197 lo.x2 = centre.x2 - size * 0.76f;
198 hi.x2 = centre.x2 + size * 0.60f;
199 }
200 else if (m_angle == VA3_PROFILE_RIGHT)
201 {
202 lo.x1 = centre.x1 - size * 0.8f;
203 hi.x1 = centre.x1 + size * 0.7f;
204 lo.x2 = centre.x2 - size * 0.8f;
205 hi.x2 = centre.x2 + size * 0.7f;
206 }
207 else if (m_angle == VA3_PROFILE_LEFT)
208 {
209 lo.x1 = centre.x1 - size * 0.7f;
210 hi.x1 = centre.x1 + size * 0.8f;
211 lo.x2 = centre.x2 - size * 0.8f;
212 hi.x2 = centre.x2 + size * 0.7f;
213 }
214 else if (m_angle == VA3_PS_FRONTAL)
215 {
216 // JL: I use the same equation as in FaceGen's photo submit.
217 lo.x1 = centre.x1 - size * 1.4f;
218 hi.x1 = centre.x1 + size * 1.4f;
219 lo.x2 = centre.x2 - size * 1.25f;
220 hi.x2 = centre.x2 + size * 1.55f;
221 }
222 else if (m_angle == VA3_PS_PROFILE_LEFT)
223 {
224 // JL: I use the same equation as in FaceGen's photo submit.
225 lo.x1 = centre.x1 - size * 0.7f;
226 hi.x1 = centre.x1 + size * 0.8f;
227 lo.x2 = centre.x2 - size * 0.8f;
228 hi.x2 = centre.x2 + size * 0.7f;
229 }
230 else if (m_angle == VA3_PS_PROFILE_RIGHT)
231 {
232 // JL: I use the same equation as in FaceGen's photo submit.
233 lo.x1 = centre.x1 - size * 0.8f;
234 hi.x1 = centre.x1 + size * 0.7f;
235 lo.x2 = centre.x2 - size * 0.8f;
236 hi.x2 = centre.x2 + size * 0.7f;
237 }
238 else
239 {
240 FGASSERT_FALSE;
241 }
242
243 return;
244}
245
246// The initial pan estimate is returned as an angle around the
247// downward axis of the face.
248//
249float Fg3InitPoints::getPan() const
250{
251 if (m_angle == VA3_VPROFILE_LEFT)
252 return fr2DegsToRads(60.0f);
253 else if (m_angle == VA3_VPROFILE_RIGHT)
254 return fr2DegsToRads(-60.0f);
255 else if (m_angle == VA3_PROFILE_LEFT)
256 return fr2DegsToRads(90.0f);
257 else if (m_angle == VA3_PROFILE_RIGHT)
258 return fr2DegsToRads(-90.0f);
259 else if (m_angle == VA3_PS_PROFILE_LEFT)
260 return fr2DegsToRads(90.0f);
261 else if (m_angle == VA3_PS_PROFILE_RIGHT)
262 return fr2DegsToRads(-90.0f);
263 return 0.0f;
264}
265
266std::string Fg3InitPoints::getLabel(LandmrkV3 type)
267{
268 FGASSERT(type < LM3_SIZE);
269 return labels[type];
270}
271
272// Expects: 4 eye points, 2 cheekbone points, 2 nare points, 2 nose points,
273// 2 mouth points, 1 chin point, 1 neck point.
274bool Fg3InitPoints::setVFrontal(vector<FutVect2IC> & pts)
275{
276 FGASSERT(pts.size() == 14);
277 m_angle = VA3_VFRONTAL;
278
279 for (uint ii=0; ii<LM3_SIZE; ii++)
280 m_valid[ii] = false;
281
282 sort(pts.begin(),pts.end(),opY); // Sort by Y val, smallest first.
283 m_pos[LM3_CHIN] = pts[pts.size()-2];
284 m_pos[LM3_THROAT_TOP] = pts[pts.size()-1];
285 m_valid[LM3_CHIN] = true;
286 m_valid[LM3_THROAT_TOP] = true;
287
288 vector<FutVect2IC> vtmp = futSubVec(pts,0,3);
289 sort(vtmp.begin(),vtmp.end(),opX); // Sort by X val, smallest first.
290 m_pos[LM3_EYE_RIGHT_OUTER] = vtmp[0];
291 m_pos[LM3_EYE_RIGHT_INNER] = vtmp[1];
292 m_pos[LM3_EYE_LEFT_INNER] = vtmp[2];
293 m_pos[LM3_EYE_LEFT_OUTER] = vtmp[3];
294 m_valid[LM3_EYE_RIGHT_OUTER] = true;
295 m_valid[LM3_EYE_RIGHT_INNER] = true;
296 m_valid[LM3_EYE_LEFT_INNER] = true;
297 m_valid[LM3_EYE_LEFT_OUTER] = true;
298
299 pts = futSubVec(pts,4,pts.size()-3);
300
301 sort(pts.begin(),pts.end(),opX);
302 m_pos[LM3_CHEEKBONE_RIGHT] = pts[0];
303 m_pos[LM3_CHEEKBONE_LEFT] = pts[pts.size()-1];
304 m_valid[LM3_CHEEKBONE_RIGHT] = true;
305 m_valid[LM3_CHEEKBONE_LEFT] = true;
306
307 pts = futSubVec(pts,1,static_cast<uint>(pts.size()-2));
308
309 sort(pts.begin(),pts.end(),opY);
310 vtmp = futSubVec(pts,pts.size()-2,pts.size()-1);
311 sort(vtmp.begin(),vtmp.end(),opX);
312 m_pos[LM3_MOUTH_RIGHT] = vtmp[0];
313 m_pos[LM3_MOUTH_LEFT] = vtmp[1];
314 m_valid[LM3_MOUTH_RIGHT] = true;
315 m_valid[LM3_MOUTH_LEFT] = true;
316
317 pts = futSubVec(pts,0,pts.size()-3);
318
319 sort(pts.begin(),pts.end(),opX);
320 m_pos[LM3_NARE_RIGHT] = pts[0];
321 m_pos[LM3_NARE_LEFT] = pts[3];
322 m_valid[LM3_NARE_RIGHT] = true;
323 m_valid[LM3_NARE_LEFT] = true;
324
325 pts = futSubVec(pts,1,2);
326
327 FGASSERT(pts.size() == 2);
328
329 sort(pts.begin(),pts.end(),opY);
330 m_pos[LM3_NOSE_TIP] = pts[0];
331 m_pos[LM3_NOSE_BASE] = pts[1];
332 m_valid[LM3_NOSE_TIP] = true;
333 m_valid[LM3_NOSE_BASE] = true;
334
335 return true;
336}
337
338// Expects: 1 brow point, 2 eye points, 1 nare point, 2 nose points,
339// 1 mouth point, 1 chin point, 1 throat point.
340bool Fg3InitPoints::setVProfile(std::vector<FutVect2IC> & pts)
341{
342 FGASSERT(pts.size() == 9);
343 for (uint ii=0; ii<LM3_SIZE; ii++)
344 m_valid[ii] = false;
345 sort(pts.begin(),pts.end(),opY); // Sort by Y val, smallest first.
346 m_pos[LM3_CHIN] = pts[7];
347 m_pos[LM3_THROAT_TOP] = pts[8];
348 m_valid[LM3_CHIN] = true;
349 m_valid[LM3_THROAT_TOP] = true;
350 vector<FutVect2IC> vtmp;
351 vtmp = futSubVec(pts,3,5);
352 sort(vtmp.begin(),vtmp.end(),opX); // Sort by X val, smallest first.
353 m_pos[LM3_NOSE_BASE] = vtmp[1];
354 m_valid[LM3_NOSE_BASE] = true;
355 vector<FutVect2IC> vtmp2;
356 vtmp2 = futSubVec(pts,1,2);
357 sort(vtmp2.begin(),vtmp2.end(),opX);
358 FutVect2IC eye = (pts[1] + pts[2]) / 2;
359 // Test the relative positions of the the nose base
360 // and the eye centre to see which profile this is.
361 //
362 if (vtmp[1].x1 < eye.x1) {
363 m_angle = VA3_VPROFILE_LEFT;
364 m_pos[LM3_BROW_RIGHT] = pts[0];
365 m_pos[LM3_NOSE_TIP] = vtmp[0];
366 m_pos[LM3_NARE_LEFT] = vtmp[2];
367 m_pos[LM3_EYE_LEFT_INNER] = vtmp2[0];
368 m_pos[LM3_EYE_LEFT_OUTER] = vtmp2[1];
369 m_pos[LM3_MOUTH_LEFT] = pts[6];
370 m_valid[LM3_BROW_RIGHT] = true;
371 m_valid[LM3_NOSE_TIP] = true;
372 m_valid[LM3_NARE_LEFT] = true;
373 m_valid[LM3_EYE_LEFT_INNER] = true;
374 m_valid[LM3_EYE_LEFT_OUTER] = true;
375 m_valid[LM3_MOUTH_LEFT] = true;
376 }
377 else {
378 m_angle = VA3_VPROFILE_RIGHT;
379 m_pos[LM3_BROW_LEFT] = pts[0];
380 m_pos[LM3_NOSE_TIP] = vtmp[2];
381 m_pos[LM3_NARE_RIGHT] = vtmp[0];
382 m_pos[LM3_EYE_RIGHT_INNER] = vtmp2[1];
383 m_pos[LM3_EYE_RIGHT_OUTER] = vtmp2[0];
384 m_pos[LM3_MOUTH_RIGHT] = pts[6];
385 m_valid[LM3_BROW_LEFT] = true;
386 m_valid[LM3_NOSE_TIP] = true;
387 m_valid[LM3_NARE_RIGHT] = true;
388 m_valid[LM3_EYE_RIGHT_OUTER] = true;
389 m_valid[LM3_EYE_RIGHT_INNER] = true;
390 m_valid[LM3_MOUTH_RIGHT] = true;
391 }
392 return true;
393}
394
395// Expects: 2 eye centre points, 1 nose tip point, 2 mouth corner points.
396bool Fg3InitPoints::setPsFrontal(vector<FutVect2IC> &pts)
397{
398 FGASSERT(pts.size() == 5);
399 for (size_t ii=0; ii<LM3_SIZE; ++ii) {
400 m_pos[ii] = FutVect2IC(0);
401 m_valid[ii] = false;
402 }
403 sort(pts.begin(),pts.end(),opY); // Sort by Y val, smallest first.
404 // Sort by X axis for the upper 2 points and the lower 2 points.
405 if (pts[0].x1 > pts[1].x1)
406 swap(pts[0],pts[1]);
407 if (pts[3].x1 > pts[4].x1)
408 swap(pts[3],pts[4]);
409 m_pos[LM3_EYE_RIGHT_CENTRE] = pts[0];
410 m_pos[LM3_EYE_LEFT_CENTRE] = pts[1];
411 m_pos[LM3_NOSE_TIP] = pts[2];
412 m_pos[LM3_MOUTH_RIGHT] = pts[3];
413 m_pos[LM3_MOUTH_LEFT] = pts[4];
414 m_valid[LM3_EYE_RIGHT_CENTRE] = true;
415 m_valid[LM3_EYE_LEFT_CENTRE] = true;
416 m_valid[LM3_NOSE_TIP] = true;
417 m_valid[LM3_MOUTH_RIGHT] = true;
418 m_valid[LM3_MOUTH_LEFT] = true;
419 m_angle = VA3_PS_FRONTAL;
420 return true;
421}
422
423// Expects: 1 outer eye point, 1 brow centre, 1 nose tip, 1 mouth corner,
424// 1 chin, 1 throat top.
425bool Fg3InitPoints::setPsProfile(vector<FutVect2IC> & pts)
426{
427 FGASSERT(pts.size() == 6);
428 for (uint ii=0; ii<LM3_SIZE; ii++)
429 m_valid[ii] = false;
430 sort(pts.begin(),pts.end(),opY);
431 m_pos[LM3_NOSE_TIP] = pts[2];
432 m_valid[LM3_NOSE_TIP] = true;
433 if (pts[2].x1 < pts[3].x1) {
434 m_angle = VA3_PS_PROFILE_LEFT;
435 m_pos[LM3_MOUTH_LEFT] = pts[3];
436 m_valid[LM3_MOUTH_LEFT] = true;
437 }
438 else {
439 m_angle = VA3_PS_PROFILE_RIGHT;
440 m_pos[LM3_MOUTH_RIGHT] = pts[3];
441 m_valid[LM3_MOUTH_RIGHT] = true;
442 }
443 vector<FutVect2IC> vtmp = futSubVec(pts,0,1);
444 sort(vtmp.begin(),vtmp.end(),opX);
445 if (m_angle == VA3_PS_PROFILE_LEFT) {
446 m_pos[LM3_BROW_CENTRE] = vtmp[0];
447 m_valid[LM3_BROW_CENTRE] = true;
448 m_pos[LM3_EYE_LEFT_OUTER] = vtmp[1];
449 m_valid[LM3_EYE_LEFT_OUTER] = true;
450 }
451 else {
452 m_pos[LM3_BROW_CENTRE] = vtmp[1];
453 m_valid[LM3_BROW_CENTRE] = true;
454 m_pos[LM3_EYE_RIGHT_OUTER] = vtmp[0];
455 m_valid[LM3_EYE_RIGHT_OUTER] = true;
456 }
457 vtmp = futSubVec(pts,4,5);
458 sort(vtmp.begin(),vtmp.end(),opX);
459 if (m_angle == VA3_PS_PROFILE_LEFT) {
460 m_pos[LM3_CHIN] = vtmp[0];
461 m_valid[LM3_CHIN] = true;
462 m_pos[LM3_THROAT_TOP] = vtmp[1];
463 m_valid[LM3_THROAT_TOP] = true;
464 }
465 else {
466 m_pos[LM3_CHIN] = vtmp[1];
467 m_valid[LM3_CHIN] = true;
468 m_pos[LM3_THROAT_TOP] = vtmp[0];
469 m_valid[LM3_THROAT_TOP] = true;
470 }
471 return true;
472}
473
474// See documentation for layout. 11 points expected.
475bool Fg3InitPoints::setV31Frontal( vector<FutVect2IC> & pts)
476{
477 FGASSERT(pts.size() == 11);
478 m_angle = VA3_FRONTAL;
479 for (uint ii=0; ii<LM3_SIZE; ii++)
480 m_valid[ii] = false;
481 sort(pts.begin(),pts.end(),opY); // Sort by Y val, smallest first.
482 m_pos[LM3_CHIN_LOWER] = pts[10];
483 m_valid[LM3_CHIN_LOWER]= true;
484 vector<FutVect2IC> vtmp = futSubVec(pts,6,9);
485 sort(vtmp.begin(),vtmp.end(),opX); // Sort by X val, smallest first.
486 m_pos[LM3_JAW_OUTER_RIGHT] = vtmp[0];
487 m_pos[LM3_MOUTH_RIGHT] = vtmp[1];
488 m_pos[LM3_MOUTH_LEFT] = vtmp[2];
489 m_pos[LM3_JAW_OUTER_LEFT] = vtmp[3];
490 m_valid[LM3_JAW_OUTER_RIGHT] = true;
491 m_valid[LM3_MOUTH_RIGHT] = true;
492 m_valid[LM3_MOUTH_LEFT] = true;
493 m_valid[LM3_JAW_OUTER_LEFT] = true;
494 pts = futSubVec(pts,0,5);
495 sort(pts.begin(),pts.end(),opX);
496 m_pos[LM3_CHEEKBONE_RIGHT] = pts[0];
497 m_pos[LM3_CHEEKBONE_LEFT] = pts[5];
498 m_valid[LM3_CHEEKBONE_RIGHT] = true;
499 m_valid[LM3_CHEEKBONE_LEFT] = true;
500 pts = futSubVec(pts,1,4);
501 sort(pts.begin(),pts.end(),opY);
502 vtmp = futSubVec(pts,2,3);
503 sort(vtmp.begin(),vtmp.end(),opX);
504 m_pos[LM3_NARE_RIGHT] = vtmp[0];
505 m_pos[LM3_NARE_LEFT] = vtmp[1];
506 m_valid[LM3_NARE_RIGHT] = true;
507 m_valid[LM3_NARE_LEFT] = true;
508 pts = futSubVec(pts,0,1);
509 sort(pts.begin(),pts.end(),opX);
510 m_pos[LM3_EYE_RIGHT_CENTRE] = pts[0];
511 m_pos[LM3_EYE_LEFT_CENTRE] = pts[1];
512 m_valid[LM3_EYE_RIGHT_CENTRE] = true;
513 m_valid[LM3_EYE_LEFT_CENTRE] = true;
514 return true;
515}
516
517// See docs for layout. Expects 9 points.
518bool Fg3InitPoints::setV31Profile(const std::vector<FutVect2IC> & ptsIn)
519{
520 vector<FutVect2IC> pts = ptsIn;
521 FGASSERT(pts.size() == 9);
522 for (uint ii=0; ii<LM3_SIZE; ii++)
523 m_valid[ii] = false;
524 vector<FutVect2IC> vtmp,vtmp2;
525 bool right; // false for left profile.
526 sort(pts.begin(),pts.end(),opY); // Sort by Y val, smallest first.
527 // Determine whether we're looking at the left or right profile.
528 vtmp = futSubVec(pts,0,2);
529 sort(vtmp.begin(),vtmp.end(),opX); // Sort by X val, smallest first.
530 vtmp2 = futSubVec(pts,3,4);
531 sort(vtmp2.begin(),vtmp2.end(),opX);
532 if (vtmp[2].x1 > vtmp2[1].x1)
533 right = false;
534 else
535 right = true;
536 if (right) {
537 m_angle = VA3_PROFILE_RIGHT;
538 m_pos[LM3_EYE_RIGHT_OUTER] = vtmp[0];
539 m_valid[LM3_EYE_RIGHT_OUTER] = true;
540 vtmp = futSubVec(vtmp,1,2);
541 }
542 else {
543 m_angle = VA3_PROFILE_LEFT;
544 m_pos[LM3_EYE_LEFT_OUTER] = vtmp[2];
545 m_valid[LM3_EYE_LEFT_OUTER] = true;
546 vtmp = futSubVec(vtmp,0,1);
547 }
548 sort(vtmp.begin(),vtmp.end(),opY);
549 m_pos[LM3_SELLION] = vtmp[0];
550 m_pos[LM3_NOSE_BRIDGE] = vtmp[1];
551 m_valid[LM3_SELLION] = true;
552 m_valid[LM3_NOSE_BRIDGE] = true;
553 vtmp = futSubVec(pts,3,4);
554 sort(vtmp.begin(),vtmp.end(),opX);
555 if (right) {
556 m_pos[LM3_NOSE_TIP] = vtmp[1];
557 m_pos[LM3_NOSE_BASE] = vtmp[0];
558 }
559 else {
560 m_pos[LM3_NOSE_TIP] = vtmp[0];
561 m_pos[LM3_NOSE_BASE] = vtmp[1];
562 }
563 m_valid[LM3_NOSE_TIP] = true;
564 m_valid[LM3_NOSE_BASE] = true;
565 vtmp = futSubVec(pts,7,8);
566 sort(vtmp.begin(),vtmp.end(),opX);
567 if (right) {
568 m_pos[LM3_THROAT_TOP] = vtmp[0];
569 m_pos[LM3_CHIN] = vtmp[1];
570 }
571 else {
572 m_pos[LM3_THROAT_TOP] = vtmp[1];
573 m_pos[LM3_CHIN] = vtmp[0];
574 }
575 m_valid[LM3_THROAT_TOP] = true;
576 m_valid[LM3_CHIN] = true;
577 m_pos[LM3_LIP_TIP_UPPER] = pts[5];
578 m_pos[LM3_LIP_TIP_LOWER] = pts[6];
579 m_valid[LM3_LIP_TIP_UPPER] = true;
580 m_valid[LM3_LIP_TIP_LOWER] = true;
581 return true;
582}
583
584bool Fg3InitPoints::empty() const
585{
586 for (uint ii=0; ii<LM3_SIZE; ++ii)
587 if (m_valid[ii])
588 return false;
589 return true;
590}
591
592NameVec2Fs Fg3InitPoints::asNameVecs() const
593{
594 NameVec2Fs ret;
595 for (size_t ii=0; ii<LM3_SIZE; ++ii) {
596 if (m_valid[ii]) {
597 FutVect2IC v = m_pos[ii];
598 Vec2I p {v[0],v[1]};
599 ret.emplace_back(labels[ii],mapCast<float>(p));
600 }
601 }
602 return ret;
603}
604
605}