FaceGen 3 SDKs Reference
Loading...
Searching...
No Matches
Fg3PhotofitUtil.cpp
1//
2// Copyright (c) 2023 Singular Inversions Inc. (facegen.com)
3// Created: May 29, 2003.
4// Authors: John Leung, Andrew Beatty
5//
6// Demonstrates the use of the PhotoFit DLL to create FG data from an image.
7//
8
9#include "stdafx.h"
10
11#include "Fg3PhotofitUtil.hpp"
12#include "FgFile.hpp"
13#include "FgFileSystem.hpp"
14#include "FgGuiApi.hpp"
15
16using namespace std;
17
18namespace Fg {
19
20#define FMOSYNTH_TOP_SL_SIZE 512
21
22Fg3InitPoints getSetLandmarks(String8 const & photoFile,ImgRgba8 const & photo)
23{
24 String8 lmsFile = pathToDirBase(photoFile)+".lms.txt";
25 Svec<FutVect2IC> lmPoss;
26 if (fileExists(lmsFile)) {
27 FaceLmPos2Fs flmPoss = toFaceLmPoss(loadLandmarks(lmsFile));
28 FaceLms flms = mapMember(flmPoss,&FaceLmPos2F::lm);
29 for (FaceView fv : getAllE<FaceView>()) {
30 FaceLms viewLms = mapMember(getV3LmAtts(fv),&FaceLmAtt::type);
31 if (containsAll(flms,viewLms)) { // this must be the view as we have all LMs for it
32 for (FaceLm lm : viewLms) {
33 FaceLmPos2F lmPos = findFirst(flmPoss,lm);
34 lmPoss.emplace_back(lmPos.pos[0],lmPos.pos[1]);
35 }
36 break;
37 }
38 }
39 }
40 Fg3InitPoints landmarks;
41 landmarks.set(lmPoss);
42 if (landmarks.empty()) {
43 fgout << fgnl <<
44 "Select the bootstrap points by left-clicking on the displayed photograph window.\n"
45 "Follow the guidlines shown in the Modeller or in the SDK documentation.\n"
46 "When finished, close the photograph window. If the window re-opens with the same\n"
47 "photograph, you have not selected the correct number of points.\n"
48 "Right-click and drag up/down to scale the image.\n";
49 bool done = false;
50 while (!done) {
51 String8 store = getDirUserAppDataLocal({"FaceGen","fg3pf"});
52 IPT<ImgRgba8> imgN = makeIPT(photo);
53 IPT<NameVec2Fs> ptsIucsN = makeIPT(NameVec2Fs{});
54 auto leftClickFn = [ptsIucsN](Vec2F iucs,Vec2UI){ptsIucsN.ref().emplace_back("",iucs); };
55 GuiPtr guiImg = guiImageCtrls(imgN,ptsIucsN,false,leftClickFn).win;
56 try {
57 guiStartImpl(makeIPT<String8>("fg3pf"),guiImg,store);
58 }
59 catch (FgExceptionNotImplemented & e) {
60 e.contexts.emplace_back("No GUI, use a .lms.txt file to specify the landmark points.");
61 throw e;
62 }
63 AxAffine2F iucsToPacs = cIucsToPacs<float>(photo.dims());
64 vector<FutVect2IC> ptsIrcs;
65 for (NameVec2F iucs : ptsIucsN.val()) {
66 Vec2F pacs = iucsToPacs * iucs.vec;
67 FutVect2IC p;
68 p.x1 = std::floor(pacs[0]);
69 p.x2 = std::floor(pacs[1]);
70 ptsIrcs.push_back(p);
71 }
72 if (ptsIrcs.empty())
73 fgThrow("User abort");
74 done = landmarks.set(ptsIrcs);
75 }
76 saveLandmarks(landmarks.asNameVecs(),lmsFile);
77 }
79 FutVect2FC lo,hi;
80 landmarks.getCropVals(lo,hi);
81 if ((hi.x2 - lo.x2) < (float)FMOSYNTH_TOP_SL_SIZE / 8.0f)
82 fgout << fgnl << "Warning: " << photoFile << " is too small for fitting";
83 else if ((hi.x2 - lo.x2) < (float)FMOSYNTH_TOP_SL_SIZE / 2.0f)
84 fgout << fgnl << "Warning: " << photoFile << " is too small for optimal fitting.";
85 if ((hi.x2 - lo.x2) < (float)FMOSYNTH_TOP_SL_SIZE)
86 fgout << fgnl << "Warning: " << photoFile << " is too small for optimal detail extraction.";
87 return landmarks;
88}
89
90void copyPoints(const Fg3InitPoints & src,FgppUserDataS & dst)
91{
92 uint numPts = 0;
93 for (int jj=0; jj<LM3_SIZE; jj++) {
94 if (src.m_valid[jj]) {
95 dst.pointX[numPts] = uint(src.m_pos[jj].x1 + 0.5f);
96 dst.pointY[numPts] = uint(src.m_pos[jj].x2 + 0.5f);
97 numPts++;
98 }
99 }
100 dst.numPoints = numPts;
101}
102
103FgppUserDataS cFgppUserData(ImgRgba8 const & photo,Fg3InitPoints const & landmarks)
104{
105 FgppUserDataS data;
106 data.width = photo.width();
107 data.height = photo.height();
108 data.widthStep = photo.width();
109 data.imageData = photo.dataPtr();
110 copyPoints(landmarks,data);
111 return data;
112}
113
114FaceLmAtts const & getV3LmAtts(FaceView view)
115{
116 static Arr<FaceLmAtts,3> ret {
117 { // front
118 {FaceLm::pupilCentreL,"EYE_LEFT_CENTRE"},
119 {FaceLm::pupilCentreR,"EYE_RIGHT_CENTRE"},
120 {FaceLm::cheekboneL,"CHEEKBONE_LEFT"},
121 {FaceLm::cheekboneR,"CHEEKBONE_RIGHT"},
122 {FaceLm::alarOuterL,"NARE_LEFT"},
123 {FaceLm::alarOuterR,"NARE_RIGHT"},
124 {FaceLm::mouthCornerL,"MOUTH_LEFT"},
125 {FaceLm::mouthCornerR,"MOUTH_RIGHT"},
126 {FaceLm::jawOuterL,"JAW_OUTER_LEFT"},
127 {FaceLm::jawOuterR,"JAW_OUTER_RIGHT"},
128 {FaceLm::chinBottom,"CHIN_LOWER"},
129 },
130 { // left
131 {FaceLm::exocanthionL,"EYE_LEFT_OUTER"},
132 {FaceLm::sellion,"SELLION"},
133 {FaceLm::noseBridgeMiddle,"NOSE_BRIDGE"},
134 {FaceLm::noseTip,"NOSE_TIP"},
135 {FaceLm::subNasale,"NOSE_BASE"},
136 {FaceLm::lipUpperFront,"LIP_TIP_UPPER"},
137 {FaceLm::lipLowerFront,"LIP_TIP_LOWER"},
138 {FaceLm::chinFront,"CHIN"},
139 {FaceLm::jawBottom,"THROAT_TOP"},
140 },
141 { // right
142 {FaceLm::exocanthionR,"EYE_RIGHT_OUTER"},
143 {FaceLm::sellion,"SELLION"},
144 {FaceLm::noseBridgeMiddle,"NOSE_BRIDGE"},
145 {FaceLm::noseTip,"NOSE_TIP"},
146 {FaceLm::subNasale,"NOSE_BASE"},
147 {FaceLm::lipUpperFront,"LIP_TIP_UPPER"},
148 {FaceLm::lipLowerFront,"LIP_TIP_LOWER"},
149 {FaceLm::chinFront,"CHIN"},
150 {FaceLm::jawBottom,"THROAT_TOP"},
151 },
152 };
153 return ret[size_t(view)];
154}
155
156ViewLmPoss parseLmsV3(Vec2Fs const & pacs)
157{
158 ViewLmPoss ret;
159 auto ltX = [](Vec2F l,Vec2F r){return l[0] < r[0]; };
160 auto ltY = [](Vec2F l,Vec2F r){return l[1] < r[1]; };
161 Vec2Fs ptsY = sortAll(pacs,ltY);
162 if (ptsY.size() == 9) { // profile
163 bool right = ptsY[8][0] < ptsY[7][0]; // compare chin front & jaw bottom X
164 ret.lms.emplace_back(FaceLm::lipUpperFront,ptsY[5]);
165 ret.lms.emplace_back(FaceLm::lipLowerFront,ptsY[6]);
166 ret.lms.emplace_back(FaceLm::chinFront,ptsY[7]);
167 ret.lms.emplace_back(FaceLm::jawBottom,ptsY[8]);
168 Vec2Fs sv05X = sortAll(cSubvec(ptsY,0,5),ltX);
169 if (right) {
170 ret.view = FaceView::right;
171 ret.lms.emplace_back(FaceLm::exocanthionR,sv05X[0]);
172 ret.lms.emplace_back(FaceLm::noseTip,sv05X[4]);
173 }
174 else {
175 ret.view = FaceView::left;
176 ret.lms.emplace_back(FaceLm::noseTip,sv05X[0]);
177 ret.lms.emplace_back(FaceLm::exocanthionL,sv05X[4]);
178 }
179 Vec2Fs sv05X13Y = sortAll(cSubvec(sv05X,1,3),ltY);
180 ret.lms.emplace_back(FaceLm::sellion,sv05X13Y[0]);
181 ret.lms.emplace_back(FaceLm::noseBridgeMiddle,sv05X13Y[1]);
182 ret.lms.emplace_back(FaceLm::subNasale,sv05X13Y[2]);
183 }
184 else if (ptsY.size() == 11) { // frontal
185 ret.view = FaceView::front;
186 ret.lms.emplace_back(FaceLm::chinBottom,ptsY.back());
187 Vec2Fs sv64X = sortAll(cSubvec(ptsY,6,4),ltX);
188 ret.lms.emplace_back(FaceLm::jawOuterR,sv64X[0]);
189 ret.lms.emplace_back(FaceLm::mouthCornerR,sv64X[1]);
190 ret.lms.emplace_back(FaceLm::mouthCornerL,sv64X[2]);
191 ret.lms.emplace_back(FaceLm::jawOuterL,sv64X[3]);
192 Vec2Fs sv06X = sortAll(cSubvec(ptsY,0,6),ltX);
193 ret.lms.emplace_back(FaceLm::cheekboneR,sv06X[0]);
194 ret.lms.emplace_back(FaceLm::cheekboneL,sv06X[5]);
195 Vec2Fs sv06X14Y = sortAll(cSubvec(sv06X,1,4),ltY);
196 Vec2Fs sv06X14Y22X = sortAll(cSubvec(sv06X14Y,2,2),ltX);
197 ret.lms.emplace_back(FaceLm::alarOuterR,sv06X14Y22X[0]);
198 ret.lms.emplace_back(FaceLm::alarOuterL,sv06X14Y22X[1]);
199 Vec2Fs sv06X14Y02X = sortAll(cSubvec(sv06X14Y,0,2),ltX);
200 ret.lms.emplace_back(FaceLm::pupilCentreR,sv06X14Y02X[0]);
201 ret.lms.emplace_back(FaceLm::pupilCentreL,sv06X14Y02X[1]);
202 }
203 else
204 fgThrow("Not a valid number of landmarks for V3");
205 return ret;
206}
207
208}
Arguments for the Photofit.
Definition FgPhotoFit.h:56
const void * imageData
Definition FgPhotoFit.h:75
unsigned int numPoints
Definition FgPhotoFit.h:82
unsigned int widthStep
Definition FgPhotoFit.h:66
unsigned int height
Height of image in pixels.
Definition FgPhotoFit.h:60
unsigned int pointY[11]
The Y values of the boostrap points in pixel coordinates (0,0) at top left pixel.
Definition FgPhotoFit.h:86
unsigned int width
Width of image in pixels.
Definition FgPhotoFit.h:58
unsigned int pointX[11]
The X values of the boostrap points in pixel coordinates (0,0) at top left pixel.
Definition FgPhotoFit.h:84