stasm_lib.cpp
11.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
// stasm_lib.cpp: interface to the Stasm library
//
// Copyright (C) 2005-2013, Stephen Milborrow
#include "stasm.h"
using namespace stasm;
const char* const stasm_VERSION = STASM_VERSION;
static vec_Mod mods_g; // the ASM model(s)
//static FaceDet facedet_g; // the face detector
//static Image img_g; // the current image
//-----------------------------------------------------------------------------
namespace stasm
{
static void CheckStasmInit(void)
{
if (mods_g.empty())
Err("Models not initialized (missing call to stasm_init?)");
}
static void ShapeToLandmarks( // convert Shape to landmarks (float *)
float* landmarks, // out
const Shape& shape) // in
{
CV_Assert(shape.rows <= stasm_NLANDMARKS);
int i;
for (i = 0; i < MIN(shape.rows, stasm_NLANDMARKS); i++)
{
landmarks[i * 2] = float(shape(i, IX));
landmarks[i * 2 + 1] = float(shape(i, IY));
}
// set remaining unused landmarks if any to 0,0
for (; i < stasm_NLANDMARKS; i++)
{
landmarks[i * 2] = 0;
landmarks[i * 2 + 1] = 0;
}
}
static const Shape LandmarksAsShape( // return a Shape
const float* landmarks) // in
{
Shape shape(stasm_NLANDMARKS, 2);
for (int i = 0; i < stasm_NLANDMARKS; i++)
{
shape(i, IX) = landmarks[i*2];
shape(i, IY) = landmarks[i*2+1];
}
return shape;
}
} // namespace stasm
//-----------------------------------------------------------------------------
int stasm_init_ext( // extended version of stasm_init
const char* datadir, // in: directory of face detector files
int trace, // in: 0 normal use, 1 trace to stdout and stasm.log
void* detparams) // in: NULL or face detector parameters
{
(void) detparams;
int returnval = 1; // assume success
CatchOpenCvErrs();
try
{
print_g = (trace != 0);
trace_g = (trace != 0);
if (mods_g.empty()) // not yet initialized?
{
if (trace)
{
// Open a log file in the current directory (if possible).
// After the log file is opened, lprintf and stasm_printf
// will print to stasm.log (as well as to stdout).
//OpenLogFile();
}
lprintf("Stasm version %s%s\n",
stasm_VERSION, trace? " Logging to stasm.log": "");
CV_Assert(datadir && datadir[0] && STRNLEN(datadir, SLEN) < SLEN);
InitMods(mods_g, datadir); // init ASM model(s)
//facedet_g.OpenFaceDetector_(datadir, detparams);
//OpenEyeMouthDetectors(mods_g, datadir);
}
CheckStasmInit();
}
catch(...)
{
returnval = 0; // a call was made to Err or a CV_Assert failed
}
UncatchOpenCvErrs();
return returnval;
}
int stasm_init( // call once, at bootup (to read models from disk)
const char* datadir, // in: directory of face detector files
int trace) // in: 0 normal use, 1 trace to stdout and stasm.log
{
return stasm_init_ext(datadir, trace, NULL);
}
int stasm_open_image_ext( // extended version of stasm_open_image
const char* img, // in: gray image data, top left corner at 0,0
int width, // in: image width
int height, // in: image height
const char* imgpath, // in: image path, used only for err msgs and debug
int multiface, // in: 0=return only one face, 1=allow multiple faces
int minwidth, // in: min face width as percentage of img width
void* user) // in: NULL or pointer to user abort func
{
(void) img;
(void) width;
(void) height;
(void) imgpath;
(void) user;
int returnval = 1; // assume success
CatchOpenCvErrs();
try
{
//CV_Assert(imgpath && STRNLEN(imgpath, SLEN) < SLEN);
CV_Assert(multiface == 0 || multiface == 1);
CV_Assert(minwidth >= 1 && minwidth <= 100);
CheckStasmInit();
//img_g = Image(height, width,(unsigned char*)img);
// call the face detector to detect the face rectangle(s)
// facedet_g.DetectFaces_(img_g, imgpath, multiface == 1, minwidth, user);
}
catch(...)
{
returnval = 0; // a call was made to Err or a CV_Assert failed
}
UncatchOpenCvErrs();
return returnval;
}
int stasm_open_image( // call once per image, detect faces
const char* img, // in: gray image data, top left corner at 0,0
int width, // in: image width
int height, // in: image height
const char* imgpath, // in: image path, used only for err msgs and debug
int multiface, // in: 0=return only one face, 1=allow multiple faces
int minwidth) // in: min face width as percentage of img width
{
return stasm_open_image_ext(img, width, height, imgpath,
multiface, minwidth, NULL);
}
int stasm_search_auto_ext( // extended version of stasm_search_auto
int* foundface, // out: 0=no more faces, 1=found face
float* landmarks, // out: x0, y0, x1, y1, ..., caller must allocate
float* estyaw, // out: NULL or pointer to estimated yaw
const char* data,
const int width,
const int height,
StasmCascadeClassifier cascade,
FaceDet &detection)
{
int returnval = 1; // assume success
*foundface = 0; // but assume no face found
try
{
CheckStasmInit();
Shape shape; // the shape with landmarks
Image face_roi; // cropped to area around startshape and possibly rotated
DetPar detpar_roi; // detpar translated to ROI frame
DetPar detpar; // params returned by face det, in img frame
// Allocate image
Image img = Image(height, width,(unsigned char*)data);
// call the face detector to detect the face rectangle(s)
if (detection.detpars_.empty())
detection.DetectFaces_(img, NULL, true, 10, NULL, cascade.faceCascade);
// Get the start shape for the next face in the image, and the ROI around it.
// The shape will be wrt the ROI frame.
if (NextStartShapeAndRoi(shape, face_roi, detpar_roi, detpar,
img, mods_g, detection, cascade))
{
// now working with maybe flipped ROI and start shape in ROI frame
*foundface = 1;
if (trace_g) // show start shape?
LogShape(RoiShapeToImgFrame(shape, face_roi, detpar_roi, detpar),
"auto_start");
// select an ASM model based on the face's yaw
const int imod = ABS(EyawAsModIndex(detpar.eyaw, mods_g));
// do the actual ASM search
shape = mods_g[imod]->ModSearch_(shape, face_roi);
shape = RoiShapeToImgFrame(shape, face_roi, detpar_roi, detpar);
// now working with non flipped start shape in image frame
RoundMat(shape);
ShapeToLandmarks(landmarks, shape);
if (estyaw)
*estyaw = float(detpar.yaw);
}
}
catch(...)
{
returnval = 0; // a call was made to Err or a CV_Assert failed
}
return returnval;
}
int stasm_search_auto(// call repeatedly to find all faces
int* foundface, // out: 0=no more faces, 1=found face
float* landmarks, // out: x0, y0, x1, y1, ..., caller must allocate
const char *data,
const int width,
const int height,
StasmCascadeClassifier cascade,
FaceDet &detection)
{
return stasm_search_auto_ext(foundface, landmarks, NULL, data, width, height, cascade, detection);
}
int stasm_search_single( // wrapper for stasm_search_auto and friends
int* foundface, // out: 0=no face, 1=found face
float* landmarks, // out: x0, y0, x1, y1, ..., caller must allocate
const char* img, // in: gray image data, top left corner at 0,0
int width, // in: image width
int height, // in: image height
StasmCascadeClassifier cascade,
const char* imgpath, // in: image path, used only for err msgs and debug
const char* datadir) // in: directory of face detector files
{
(void) datadir;
(void) imgpath;
FaceDet detection;
return stasm_search_auto(foundface, landmarks, img, width, height, cascade, detection);
}
int stasm_search_pinned( // call after the user has pinned some points
float* landmarks, // out: x0, y0, x1, y1, ..., caller must allocate
const float* pinned, // in: pinned landmarks (0,0 points not pinned)
const char* data, // in: gray image data, top left corner at 0,0
int width, // in: image width
int height, // in: image height
const char* imgpath) // in: image path, used only for err msgs and debug
{
(void) width;
(void) height;
(void) imgpath;
int returnval = 1; // assume success
try
{
CheckStasmInit();
Image img = Image(height, width,(unsigned char*)data);
const Shape pinnedshape(LandmarksAsShape(pinned));
Shape shape; // the shape with landmarks
Image face_roi; // img cropped to startshape area and maybe rotated
Shape pinned_roi; // pinned translated to ROI frame
DetPar detpar_roi; // detpar translated to ROI frame
DetPar detpar; // params returned by pseudo face det, in img frame
PinnedStartShapeAndRoi(shape, face_roi, detpar_roi, detpar, pinned_roi, img, mods_g, pinnedshape);
// now working with maybe flipped ROI and start shape in ROI frame
const int imod = ABS(EyawAsModIndex(detpar.eyaw, mods_g));
shape = mods_g[imod]->ModSearch_(shape, face_roi, &pinned_roi); // ASM search
shape = RoiShapeToImgFrame(shape, face_roi, detpar_roi, detpar);
// now working with non flipped start shape in image frame
RoundMat(shape);
ForcePinnedPoints(shape, pinnedshape); // undo above RoundMat on pinned points
ShapeToLandmarks(landmarks, shape);
}
catch(...)
{
returnval = 0; // a call was made to Err or a CV_Assert failed
}
return returnval;
}
const char* stasm_lasterr(void) // same as LastErr but not in stasm namespace
{
return LastErr(); // return the last error message (stashed in sgErr)
}
void stasm_force_points_into_image( // force landmarks into image boundary
float* landmarks, // io
int ncols, // in
int nrows) // in
{
for (int i = 0; i < stasm_NLANDMARKS; i++)
{
landmarks[i * 2] = Clamp(landmarks[i * 2], 0.f, float(ncols-1));
landmarks[i * 2 + 1] = Clamp(landmarks[i * 2 + 1], 0.f, float(nrows-1));
}
}
void stasm_convert_shape( // convert stasm 77 points to given number of points
float* landmarks, // io: return all points zero if can't do conversion
int nlandmarks) // in: 77=nochange 76=stasm3 68=xm2vts 22=ar 20=bioid 17=me17
{
Shape newshape = ConvertShape(LandmarksAsShape(landmarks), nlandmarks);
if (newshape.rows)
ShapeToLandmarks(landmarks, newshape);
else // cannot convert, set all points to 0,0
for (int i = 0; i < stasm_NLANDMARKS; i++)
landmarks[i * 2] = landmarks[i * 2 + 1] = 0;
}
void stasm_printf( // print to stdout and to the stasm.log file if it is open
const char* format, // args like printf
...)
{
char s[SBIG];
va_list args;
va_start(args, format);
VSPRINTF(s, format, args);
va_end(args);
lputs(s);
}