FaceGen 3 SDKs Reference
Loading...
Searching...
No Matches
Fg3CmdUtil.cpp
1//
2// Copyright (c) Singular Inversions Inc. 2011
3//
4// Authors: Andrew Beatty
5// Created: October 3, 2011
6//
7
8#include "stdafx.h"
9
10#include "FgCommand.hpp"
11#include "Fg3Sam.hpp"
12#include "Fg3dMeshIo.hpp"
13#include "FgSerial.hpp"
14#include "algs.hpp"
15#include "matrixCT.hpp"
16#include "FgTransform.hpp"
17#include "FgTime.hpp"
18#include "bootPoints.hpp"
19#include "FgParse.hpp"
20#include "FgTestUtils.hpp"
21#include "FgAnthropometry.hpp"
22#include "Fg3PhotofitUtil.hpp"
23
24using namespace std;
25
26namespace Fg {
27
28namespace {
29
30void maskFim(CLArgs const & args)
31{
32 Syntax
33 syn(args,
34 "<in>.fim <mask>.<img> <out>.fim\n"
35 " <img> = " + clOptionsStr(getImgExts()) + "\n"
36 " The mask image is made monochrome and thresholded in the middle");
37 if (args.size() != 4)
38 syn.errorNumArgs();
39 syn.checkExtension(args[1],"fim");
40 syn.checkExtension(args[2],getImgExts());
41 syn.checkExtension(args[3],"fim");
42 Img2F in = loadFim(args[1]),out;
43 ImgRgba8 mask = loadImage(args[2]);
44 if (mask.dims() != in.dims())
45 fgThrow("fim and mask dimensions do not match");
46 out = in;
47 for (uint yy=0; yy<mask.height(); ++yy) {
48 for (uint xx=0; xx<mask.width(); ++xx) {
49 uchar mval = mask.xy(xx,yy).rec709();
50 uint idx = yy*out.width()+xx;
51 if (mval > 127)
52 out.m_data[idx] = in.m_data[idx];
53 else
54 out.m_data[idx] = Vec2F(-1.0f,-1.0f);
55 }
56 }
57 Ofstream ofs(args[3]);
58 ofs.write("FIMFF001",8);
59 ofs.write((char*)&out.m_dims[0],4);
60 ofs.write((char*)&out.m_dims[1],4);
61 char reserve[48];
62 memset(&reserve[0],0,sizeof(reserve));
63 ofs.write(reserve,48);
64 ofs.write((char *)out.dataPtr(),out.numPixels()*sizeof(FutVect2FC));
65}
66
67void cmdResample(CLArgs const & args)
68{
69 Syntax syn(args,
70 "<transform>.fim <in>.<img> <out>.<img>\n"
71 "<transform>.fim - Created by Customizer\n"
72 "<img> - " + clOptionsStr(getImgExts())
73 );
74 Img2F fim = loadFim(syn.next());
75 ImgRgba8 img = loadImage(syn.next()),
76 ret = resampleMap(fim,img);
77 saveImage(ret,syn.next());
78}
79
80void egtToImgs(CLArgs const & args)
81{
82 Syntax syn(args,
83 "<sam>.egt\n"
84 " Saves each mode as a separate image <sam>_egt_(S|A)_XX.png, in which the signed char values\n"
85 " are mapped to unsigned by lifting 0 to 128."
86 );
87 string egtName = syn.next();
88 Scm3SA egt = loadEgt(egtName);
89 Path samPath(egtName);
90 for (uint ss=0; ss<2; ++ss) {
91 const vector<Scm3Mode> & modes = (ss == 0) ? egt.symm : egt.asym;
92 for (size_t ii=0; ii<modes.size(); ++ii) {
93 const Scm3Mode & mode = modes[ii];
94 string symm = (ss == 0) ? "S" : "A";
95 fgout << fgnl << symm << " " << ii << " scale: " << mode.scale;
96 ImgRgba8 img(mode.mode.dims());
97 for (size_t jj=0; jj<img.numPixels(); ++jj) {
98 Arr3SC md = mode.mode.m_data[jj];
99 Rgba8 col(0,0,0,255);
100 for (uint cc=0; cc<3; ++cc)
101 col[cc] = scast<uchar>(scast<int>(md[cc])+128);
102 img.m_data[jj] = col;
103 }
104 saveImage(img,samPath.dirBase()+"_egt_"+symm+"_"+toStr(ii)+".png");
105 }
106 }
107}
108
109void xformSsm(CLArgs const & args)
110{
111 Syntax syn(args,
112 "<transform>.txt <in> <out>\n"
113 " <transform>.txt is created with the 'xform create' command\n"
114 " <in>.tri and <in>.egm must exist.\n"
115 " <out>.tri and <out>.egm will be written.\n"
116 );
117 SimilarityD xform = dsrlzText<SimilarityD>(loadRawString(syn.next()));
118 string baseName = syn.next();
119 Mesh mesh = loadTri(baseName+".tri");
120 Ssm3 ssm(mesh.verts,baseName+".egm");
121 mesh.transform_(xform);
122 ssm.transform(xform);
123 baseName = syn.next();
124 saveTri(baseName+".tri",mesh);
125 ssm.saveModes(baseName+".egm");
126}
127
128void cmdSsmZero(CLArgs const & args)
129{
130 Syntax syn {args,
131 R"(<marked>.<mesh> <ssmIn>.egm <ssmOut>.egm
132 <marked>.<mesh> - must have the vertices whose SSM values are to be set to zero marked.
133 <mesh> - (tri,fgmesh)
134 <ssmIn>.egm - will be modified to this effect.
135NOTES:
136 * specified vertices will no longer be moved by FaceGen SSM statistics.
137 * prefer the fixed seam vertices arguments in the 'ssm' command as that smoothly influences the
138 surrounding space.)"
139 };
140 Mesh mesh = loadMesh(syn.next());
141 if (mesh.markedVerts.empty())
142 syn.error("No marked vertices in mesh",syn.curr()+".tri");
143 Ssm3 ssm {mesh.verts,syn.next()};
144 if (ssm.empty())
145 syn.error("No SSM modes found",syn.curr());
146 for (uint ss=0; ss<2; ++ss) {
147 Vec3Fss & modes = (ss == 0) ? ssm.symmModes : ssm.asymModes;
148 for (size_t mm=0; mm<modes.size(); ++mm) {
149 Vec3Fs & mode = modes[mm];
150 if (mode.size() < mesh.verts.size())
151 syn.error("EGM size too small for this TRI");
152 for (size_t ii=0; ii<mesh.markedVerts.size(); ++ii)
153 mode[mesh.markedVerts[ii].idx] = Vec3F(0);
154 }
155 }
156 ssm.saveModes(syn.curr());
157}
158
159void comvertLms(CLArgs const & args)
160{
161 Syntax syn {args,R"((<base>.bpt.xml | <in>.lms.txt)
162OUTPUT:
163 <in>.lms.txt - parsed (and converted if bpt.xml) landmarks in simple text format (aka YOLO format)
164NOTES:
165 Only applies to FaceGen V3 landmarks; 11 for frontal and 9 for profile.)"
166 };
167 Path path {syn.next()};
168 syn.noMoreArgsExpected();
169 Vec2Fs poss;
170 if (path.ext == "xml") {
171 Strings oldStrs = splitLines(loadRawString(path.str()));
172 for (String const & os : oldStrs) {
173 Strings toks = tokenize(os);
174 if (toks.size() == 8) { // < column > ### < / column >
175 if (toks[1] == "column")
176 poss.push_back(Vec2F{fromStr<float>(toks[3]).value(),0});
177 else if (toks[1] == "row")
178 poss.back()[1] = fromStr<float>(toks[3]).value();
179 }
180 }
181 }
182 else if (path.ext == "txt")
183 poss = mapMember(loadLandmarks(path.str()),&NameVec2F::vec); // ditch names and re-parse
184 else
185 syn.error("Unrecognized LMS file type");
186 ViewLmPoss fls = parseLmsV3(poss);
187 NameVec2Fs nvs = toNameVecs(fls.lms);
188 path.base = cSubstr8(path.base,0,path.base.size()-4);
189 saveLandmarks(nvs,path.dirBase()+".lms.txt");
190 fgout << fgnl << fls.lms.size() << " points parsed for " << fls.view << " view";
191}
192
193void cmdSsmSplice(CLArgs const & args)
194{
195 Syntax syn {args,
196 R"(<src1>.egm <src2>.egm <inds>.txt <dst>.egm
197 <src1>.egm - take deformations from this EGM (skin deformation)
198 <src2>.egm - replace deformations from this EGM (eye motions)
199 <inds>.txt - replace only the deformations for these indices
200 <dst>.egm - write the combined deformations here
201NOTES:
202 <src1> and <src2> must have the same vertex count)"
203 };
204 Vec3Fss src1S,src1A,src2S,src2A;
205 uint32 V = loadEgm_(syn.next(),src1S,src1A),
206 V2 = loadEgm_(syn.next(),src2S,src2A);
207 if (V != V2)
208 syn.error("<src1> and <src2> have different vertex counts",toStr(V)+"!="+toStr(V2));
209 FGASSERT(src1S.size() == src2S.size());
210 FGASSERT(src1A.size() == src2A.size());
211 Vec3Fss dstS = src1S,
212 dstA = src1A;
213 Strings strs = splitWhitespace(loadRawString(syn.next()));
214 for (String const & str : strs) {
215 Opt<uint> ou = fromStr<uint>(str);
216 if (!ou.has_value())
217 syn.error("Invalid value in <inds>",str);
218 uint idx = ou.value();
219 for (size_t ss=0; ss<src1S.size(); ++ss)
220 src1S[ss][idx] = src2S[ss][idx];
221 for (size_t aa=0; aa<src1A.size(); ++aa)
222 src1A[aa][idx] = src2A[aa][idx];
223 }
224 saveEgm(syn.next(),dstS,dstA);
225}
226
227}
228
229Cmd getCmd3EndianToggle();
230
235void cmd3Util(CLArgs const & args)
236{
237 Cmds cmds {
238 {comvertLms,"lms","Convert old bpt.xml image landmark files to simpler .lms.txt"},
239 {egtToImgs,"egtToImgs","Convert an EGT into a series of images for examination"},
240 {getCmd3EndianToggle()},
241 {cmdGetInternal,"internal","Save a copy of the FaceGen internal base face in the current directory"},
242 {maskFim,"maskFim","Mask an image transform map (.fim) to set some areas to invalid"},
243 {cmdResample,"resample","Resample an image based on an image transform map in the .FIM format"},
244 {cmdSsmSplice,"ssmSplice","Splice together vertex deformations from different EGMs (for eye motions)"},
245 {cmdSsmZero,"ssmZero","Set the SSM values of the marked vertices to zero"},
246 {xformSsm,"xformSsm","Apply a similarity transform to a SSM (.TRI and .EGM)"},
247 };
248 doMenu(args,cmds);
249}
250
251}
uint32 loadEgm_(String8 const &fname, Vec3Fss &symm, Vec3Fss &asym)
Load statistical shape modes from .EGM file into last two arguments. Returns number of vertices.
void cmd3Util(CLArgs const &)