Commit 3f3a0a7b3def15208510026e4c53a27b8d13b3cb

Authored by Scott Klum
1 parent 7693fd5f

Stasm properly uses internal detector to landmark all faces in image

3rdparty/stasm/stasm/CMakeLists.txt deleted
1 -# Stasm CMakeLists  
2 -  
3 -aux_source_directory(stasm/src STASM_SRC)  
4 -include_directories(stasm/include)  
5 -  
6 -#aux_source_directory(tasm/src TASM_SRC)  
7 -#include_directories(tasm/include)  
8 -  
9 -add_library(stasm SHARED ${STASM_SRC} ${TASM_SRC})  
10 -qt5_use_modules(stasm ${QT_DEPENDENCIES})  
11 -  
12 -set_target_properties(stasm PROPERTIES  
13 - DEFINE_SYMBOL STASM_LIBRARY  
14 - VERSION ${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}  
15 - SOVERSION ${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}  
16 - LINK_INTERFACE_LIBRARIES ""  
17 - AUTOMOC TRUE)  
18 -  
19 -target_link_libraries(stasm ${OpenCV_LIBS} ${Qt5Core_QTMAIN_LIBRARIES})  
20 -  
21 -file(GLOB STASM_HEADERS stasm/include/*.h)  
22 -install(FILES ${STASM_HEADERS} DESTINATION include/stasm)  
23 -file(GLOB CLASSIC_HEADERS stasm/include/classic/*.mh)  
24 -install(FILES ${CLASSIC_HEADERS} DESTINATION include/stasm/classic)  
25 -file(GLOB HAT_HEADERS stasm/include/hat/*.mh)  
26 -install(FILES ${HAT_HEADERS} DESTINATION include/stasm/hat)  
27 -#file(GLOB TASM_HEADERS tasm/include/*.h)  
28 -#install(FILES ${TASM_HEADERS} DESTINATION include/tasm)  
29 -  
30 -install(TARGETS stasm RUNTIME DESTINATION bin LIBRARY DESTINATION lib ARCHIVE DESTINATION lib)  
3rdparty/stasm/stasm/stasm/src/print.cpp deleted
1 -// print.cpp: printing and logging utilities for the Stasm library  
2 -//  
3 -// Copyright (C) 2005-2013, Stephen Milborrow  
4 -  
5 -#include "print.h"  
6 -#include "err.h"  
7 -#include "misc.h"  
8 -  
9 -#include <stdio.h>  
10 -#include <sys/stat.h>  
11 -#include <stdarg.h>  
12 -  
13 -namespace stasm  
14 -{  
15 -bool print_g; // true to allow output to stdout (but error msgs always printed)  
16 -  
17 -bool trace_g; // true to trace Stasm internal operation  
18 -  
19 -static FILE* logfile_g; // lprintfs go to this log file as well as stdout  
20 -  
21 -//-----------------------------------------------------------------------------  
22 -  
23 -// Open the log file. After this, when you call lprintf, you print to the log  
24 -// file (as well as to stdout). This inits the global variable logfile_g.  
25 -  
26 -void OpenLogFile( // also inits the global variable logfile_g  
27 - const char* path) // in: log file path, default is "stasm.log"  
28 -{  
29 - if (!logfile_g)  
30 - {  
31 - if (print_g)  
32 - printf("Generating %s\n", path);  
33 - logfile_g = fopen(path, "wb");  
34 - if (!logfile_g)  
35 - Err("Cannot open \"%s\"", path);  
36 - // check that we can write to the log file  
37 - if (fputs("log file\n", logfile_g) < 0)  
38 - Err("Cannot write to \"%s\"", path);  
39 - rewind(logfile_g); // rewind so above test msg is not in the log file  
40 - }  
41 -}  
42 -  
43 -// Like printf but only prints if print_g flag is set.  
44 -// Also prints to the log file if it is open (regardless of print_g).  
45 -  
46 -void lprintf(const char* format, ...) // args like printf  
47 -{  
48 - char s[SBIG];  
49 - va_list args;  
50 - va_start(args, format);  
51 - VSPRINTF(s, format, args);  
52 - va_end(args);  
53 - if (print_g)  
54 - {  
55 - printf("%s", s);  
56 - fflush(stdout); // flush so if there is a crash we can see what happened  
57 - }  
58 - if (logfile_g)  
59 - {  
60 - // we don't check fputs here, to prevent recursive calls and msgs  
61 - fputs(s, logfile_g);  
62 - fflush(logfile_g);  
63 - }  
64 -}  
65 -  
66 -// Like printf but prints to the log file only (and not to stdout).  
67 -// Used for detailed stuff that we don't usually want to see.  
68 -  
69 -void logprintf(const char* format, ...) // args like printf  
70 -{  
71 - if (logfile_g)  
72 - {  
73 - char s[SBIG];  
74 - va_list args;  
75 - va_start(args, format);  
76 - VSPRINTF(s, format, args);  
77 - va_end(args);  
78 - // we don't check fputs here, to prevent recursive calls and msgs  
79 - fputs(s, logfile_g);  
80 - fflush(logfile_g);  
81 - }  
82 -}  
83 -  
84 -// Like lprintf but always prints even if print_g is false.  
85 -  
86 -void lprintf_always(const char* format, ...)  
87 -{  
88 - char s[SBIG];  
89 - va_list args;  
90 - va_start(args, format);  
91 - VSPRINTF(s, format, args);  
92 - va_end(args);  
93 - printf("%s", s);  
94 - fflush(stdout); // flush so if there is a crash we can see what happened  
95 - if (logfile_g)  
96 - {  
97 - // we don't check fputs here, to prevent recursive calls and msgs  
98 - fputs(s, logfile_g);  
99 - fflush(logfile_g);  
100 - }  
101 -}  
102 -  
103 -// Like puts but prints to the log file as well if it is open,  
104 -// and does not append a newline.  
105 -  
106 -void lputs(const char* s)  
107 -{  
108 - printf("%s", s);  
109 - fflush(stdout); // flush so if there is a crash we can see what happened  
110 - logprintf("%s", s);  
111 -}  
112 -  
113 -// Print message only once on the screen, and only 100 times to the log file.  
114 -// This is used when similar messages could be printed many times and it  
115 -// suffices to let the user know just once. By convention the message is  
116 -// printed followed by "..." so the user knows that just the first message  
117 -// was printed. The user can look in the log file for further messages if  
118 -// necessary (but we print only 100 times to the log file --- else all the  
119 -// log prints make tasm slow).  
120 -  
121 -void PrintOnce(  
122 - int& printed, // io: zero=print, nonzero=no print  
123 - const char* format, ...) // in: args like printf  
124 -{  
125 - char s[SBIG];  
126 - va_list args;  
127 - va_start(args, format);  
128 - VSPRINTF(s, format, args);  
129 - va_end(args);  
130 - if (printed == 0 && print_g)  
131 - {  
132 - printed = 1;  
133 - printf("%s", s);  
134 - fflush(stdout); // flush so if there is a crash we can see what happened  
135 - }  
136 - if (printed < 100 && logfile_g)  
137 - {  
138 - fputs(s, logfile_g);  
139 - fflush(logfile_g);  
140 - printed++;  
141 - if (printed == 100)  
142 - logprintf("no more prints of the above message (printed == 100)\n");  
143 - }  
144 -}  
145 -  
146 -} // namespace stasm  
3rdparty/stasm4.0.0/stasm/MOD_1/facedet.cpp
@@ -47,28 +47,16 @@ void DetectFaces( // all face rects into detpars @@ -47,28 +47,16 @@ void DetectFaces( // all face rects into detpars
47 int minwidth, 47 int minwidth,
48 cv::CascadeClassifier cascade) // in: as percent of img width 48 cv::CascadeClassifier cascade) // in: as percent of img width
49 { 49 {
50 - int leftborder = 0, topborder = 0; // border size in pixels  
51 - Image bordered_img(BORDER_FRAC == 0?  
52 - img: EnborderImg(leftborder, topborder, img));  
53 -  
54 - // Detection results are very slightly better with equalization  
55 - // (tested on the MUCT images, which are not pre-equalized), and  
56 - // it's quick enough to equalize (roughly 10ms on a 1.6 GHz laptop).  
57 -  
58 - Image equalized_img; cv::equalizeHist(bordered_img, equalized_img);  
59 -  
60 CV_Assert(minwidth >= 1 && minwidth <= 100); 50 CV_Assert(minwidth >= 1 && minwidth <= 100);
61 51
62 - int minpix = MAX(100, cvRound(img.cols * minwidth / 100.));  
63 -  
64 // the params below are accurate but slow 52 // the params below are accurate but slow
65 - static const double SCALE_FACTOR = 1.1;  
66 - static const int MIN_NEIGHBORS = 3; 53 + static const double SCALE_FACTOR = 1.2;
  54 + static const int MIN_NEIGHBORS = 5;
67 static const int DETECTOR_FLAGS = 0; 55 static const int DETECTOR_FLAGS = 0;
68 56
69 vec_Rect facerects = // all face rects in image 57 vec_Rect facerects = // all face rects in image
70 - Detect(equalized_img, &cascade, NULL,  
71 - SCALE_FACTOR, MIN_NEIGHBORS, DETECTOR_FLAGS, minpix); 58 + Detect(img, &cascade, NULL,
  59 + SCALE_FACTOR, MIN_NEIGHBORS, DETECTOR_FLAGS, 64);
72 60
73 // copy face rects into the detpars vector 61 // copy face rects into the detpars vector
74 62
@@ -80,8 +68,6 @@ void DetectFaces( // all face rects into detpars @@ -80,8 +68,6 @@ void DetectFaces( // all face rects into detpars
80 // detpar.x and detpar.y is the center of the face rectangle 68 // detpar.x and detpar.y is the center of the face rectangle
81 detpar.x = facerect->x + facerect->width / 2.; 69 detpar.x = facerect->x + facerect->width / 2.;
82 detpar.y = facerect->y + facerect->height / 2.; 70 detpar.y = facerect->y + facerect->height / 2.;
83 - detpar.x -= leftborder; // discount the border we added earlier  
84 - detpar.y -= topborder;  
85 detpar.width = double(facerect->width); 71 detpar.width = double(facerect->width);
86 detpar.height = double(facerect->height); 72 detpar.height = double(facerect->height);
87 detpar.yaw = 0; // assume face has no yaw in this version of Stasm 73 detpar.yaw = 0; // assume face has no yaw in this version of Stasm
@@ -187,10 +173,10 @@ void FaceDet::DetectFaces_( // call once per image to find all the faces @@ -187,10 +173,10 @@ void FaceDet::DetectFaces_( // call once per image to find all the faces
187 //CV_Assert(!facedet_g.empty()); // check that OpenFaceDetector_ was called 173 //CV_Assert(!facedet_g.empty()); // check that OpenFaceDetector_ was called
188 174
189 DetectFaces(detpars_, img, minwidth, cascade); 175 DetectFaces(detpars_, img, minwidth, cascade);
190 - DiscardMissizedFaces(detpars_); 176 + //DiscardMissizedFaces(detpars_);
191 if (multiface) // order faces on increasing distance from left margin 177 if (multiface) // order faces on increasing distance from left margin
192 { 178 {
193 - sort(detpars_.begin(), detpars_.end(), IncreasingLeftMargin); 179 + sort(detpars_.begin(), detpars_.end(), DecreasingWidth);
194 } 180 }
195 else 181 else
196 { 182 {
3rdparty/stasm4.0.0/stasm/MOD_1/facedet.h
@@ -39,9 +39,9 @@ public: @@ -39,9 +39,9 @@ public:
39 FaceDet() {} // constructor 39 FaceDet() {} // constructor
40 40
41 41
42 -private:  
43 vector<DetPar> detpars_; // all the valid faces in the current image 42 vector<DetPar> detpars_; // all the valid faces in the current image
44 43
  44 +private:
45 int iface_; // index of current face for NextFace_ 45 int iface_; // index of current face for NextFace_
46 // indexes into detpars_ 46 // indexes into detpars_
47 47
3rdparty/stasm4.0.0/stasm/stasm_lib.cpp
@@ -160,7 +160,8 @@ int stasm_search_auto_ext( // extended version of stasm_search_auto @@ -160,7 +160,8 @@ int stasm_search_auto_ext( // extended version of stasm_search_auto
160 const char* data, 160 const char* data,
161 const int width, 161 const int width,
162 const int height, 162 const int height,
163 - StasmCascadeClassifier cascade) 163 + StasmCascadeClassifier cascade,
  164 + FaceDet &detection)
164 { 165 {
165 int returnval = 1; // assume success 166 int returnval = 1; // assume success
166 *foundface = 0; // but assume no face found 167 *foundface = 0; // but assume no face found
@@ -176,15 +177,14 @@ int stasm_search_auto_ext( // extended version of stasm_search_auto @@ -176,15 +177,14 @@ int stasm_search_auto_ext( // extended version of stasm_search_auto
176 // Allocate image 177 // Allocate image
177 Image img = Image(height, width,(unsigned char*)data); 178 Image img = Image(height, width,(unsigned char*)data);
178 179
179 - FaceDet facedet;  
180 -  
181 // call the face detector to detect the face rectangle(s) 180 // call the face detector to detect the face rectangle(s)
182 - facedet.DetectFaces_(img, NULL, false, 10, NULL, cascade.faceCascade); 181 + if (detection.detpars_.empty())
  182 + detection.DetectFaces_(img, NULL, true, 10, NULL, cascade.faceCascade);
183 183
184 // Get the start shape for the next face in the image, and the ROI around it. 184 // Get the start shape for the next face in the image, and the ROI around it.
185 // The shape will be wrt the ROI frame. 185 // The shape will be wrt the ROI frame.
186 if (NextStartShapeAndRoi(shape, face_roi, detpar_roi, detpar, 186 if (NextStartShapeAndRoi(shape, face_roi, detpar_roi, detpar,
187 - img, mods_g, facedet, cascade)) 187 + img, mods_g, detection, cascade))
188 { 188 {
189 // now working with maybe flipped ROI and start shape in ROI frame 189 // now working with maybe flipped ROI and start shape in ROI frame
190 *foundface = 1; 190 *foundface = 1;
@@ -219,9 +219,10 @@ int stasm_search_auto(// call repeatedly to find all faces @@ -219,9 +219,10 @@ int stasm_search_auto(// call repeatedly to find all faces
219 const char *data, 219 const char *data,
220 const int width, 220 const int width,
221 const int height, 221 const int height,
222 - StasmCascadeClassifier cascade) 222 + StasmCascadeClassifier cascade,
  223 + FaceDet &detection)
223 { 224 {
224 - return stasm_search_auto_ext(foundface, landmarks, NULL, data, width, height, cascade); 225 + return stasm_search_auto_ext(foundface, landmarks, NULL, data, width, height, cascade, detection);
225 } 226 }
226 227
227 int stasm_search_single( // wrapper for stasm_search_auto and friends 228 int stasm_search_single( // wrapper for stasm_search_auto and friends
@@ -237,7 +238,8 @@ int stasm_search_single( // wrapper for stasm_search_auto and friends @@ -237,7 +238,8 @@ int stasm_search_single( // wrapper for stasm_search_auto and friends
237 (void) datadir; 238 (void) datadir;
238 (void) imgpath; 239 (void) imgpath;
239 240
240 - return stasm_search_auto(foundface, landmarks, img, width, height, cascade); 241 + FaceDet detection;
  242 + return stasm_search_auto(foundface, landmarks, img, width, height, cascade, detection);
241 } 243 }
242 244
243 int stasm_search_pinned( // call after the user has pinned some points 245 int stasm_search_pinned( // call after the user has pinned some points
3rdparty/stasm4.0.0/stasm/stasm_lib.h
@@ -64,6 +64,10 @@ @@ -64,6 +64,10 @@
64 64
65 #include "stasmcascadeclassifier.h" 65 #include "stasmcascadeclassifier.h"
66 66
  67 +namespace stasm {
  68 + class FaceDet;
  69 +}
  70 +
67 static const int stasm_NLANDMARKS = 77; // number of landmarks 71 static const int stasm_NLANDMARKS = 77; // number of landmarks
68 72
69 extern const char* const stasm_VERSION; 73 extern const char* const stasm_VERSION;
@@ -89,7 +93,8 @@ int stasm_search_auto( // call repeatedly to find all faces @@ -89,7 +93,8 @@ int stasm_search_auto( // call repeatedly to find all faces
89 const char* data, 93 const char* data,
90 const int width, 94 const int width,
91 const int height, 95 const int height,
92 - StasmCascadeClassifier cascade); 96 + StasmCascadeClassifier cascade,
  97 + stasm::FaceDet &detection);
93 98
94 extern "C" 99 extern "C"
95 int stasm_search_single( // wrapper for stasm_search_auto and friends 100 int stasm_search_single( // wrapper for stasm_search_auto and friends
openbr/plugins/metadata/stasm4.cpp
1 #include <QString> 1 #include <QString>
2 -#include <stasm_lib.h>  
3 #include <stasmcascadeclassifier.h> 2 #include <stasmcascadeclassifier.h>
  3 +#include <stasm_lib.h>
  4 +#include <stasm.h>
4 #include <opencv2/opencv.hpp> 5 #include <opencv2/opencv.hpp>
5 6
6 #include <openbr/plugins/openbr_internal.h> 7 #include <openbr/plugins/openbr_internal.h>
@@ -56,14 +57,12 @@ BR_REGISTER(Initializer, StasmInitializer) @@ -56,14 +57,12 @@ BR_REGISTER(Initializer, StasmInitializer)
56 * \brief Wraps STASM key point detector 57 * \brief Wraps STASM key point detector
57 * \author Scott Klum \cite sklum 58 * \author Scott Klum \cite sklum
58 */ 59 */
59 -class StasmTransform : public UntrainableTransform 60 +class StasmTransform : public UntrainableMetaTransform
60 { 61 {
61 Q_OBJECT 62 Q_OBJECT
62 63
63 Q_PROPERTY(bool stasm3Format READ get_stasm3Format WRITE set_stasm3Format RESET reset_stasm3Format STORED false) 64 Q_PROPERTY(bool stasm3Format READ get_stasm3Format WRITE set_stasm3Format RESET reset_stasm3Format STORED false)
64 BR_PROPERTY(bool, stasm3Format, false) 65 BR_PROPERTY(bool, stasm3Format, false)
65 - Q_PROPERTY(bool clearLandmarks READ get_clearLandmarks WRITE set_clearLandmarks RESET reset_clearLandmarks STORED false)  
66 - BR_PROPERTY(bool, clearLandmarks, false)  
67 Q_PROPERTY(QList<float> pinPoints READ get_pinPoints WRITE set_pinPoints RESET reset_pinPoints STORED false) 66 Q_PROPERTY(QList<float> pinPoints READ get_pinPoints WRITE set_pinPoints RESET reset_pinPoints STORED false)
68 BR_PROPERTY(QList<float>, pinPoints, QList<float>()) 67 BR_PROPERTY(QList<float>, pinPoints, QList<float>())
69 Q_PROPERTY(QStringList pinLabels READ get_pinLabels WRITE set_pinLabels RESET reset_pinLabels STORED false) 68 Q_PROPERTY(QStringList pinLabels READ get_pinLabels WRITE set_pinLabels RESET reset_pinLabels STORED false)
@@ -77,86 +76,104 @@ class StasmTransform : public UntrainableTransform @@ -77,86 +76,104 @@ class StasmTransform : public UntrainableTransform
77 stasmCascadeResource.setResourceMaker(new StasmResourceMaker()); 76 stasmCascadeResource.setResourceMaker(new StasmResourceMaker());
78 } 77 }
79 78
80 - void project(const Template &src, Template &dst) const 79 + QList<QPointF> convertLandmarks(int nLandmarks, float *landmarks) const
81 { 80 {
82 - Mat stasmSrc(src);  
83 - if (src.m().channels() == 3)  
84 - cvtColor(src, stasmSrc, CV_BGR2GRAY);  
85 - else if (src.m().channels() != 1)  
86 - qFatal("Stasm expects single channel matrices.");  
87 -  
88 - if (!stasmSrc.isContinuous())  
89 - qFatal("Stasm expects continuous matrix data.");  
90 - dst = src;  
91 -  
92 - int foundFace = 0;  
93 - int nLandmarks = stasm_NLANDMARKS;  
94 - float landmarks[2 * stasm_NLANDMARKS];  
95 -  
96 - bool searchPinned = false;  
97 -  
98 - QPointF rightEye, leftEye;  
99 - /* Two use cases are accounted for:  
100 - * 1. Pin eyes without normalization: in this case the string list should contain the KEYS for right then left eyes, respectively.  
101 - * 2. Pin eyes with normalization: in this case the string list should contain the COORDINATES of the right then left eyes, respectively.  
102 - * Currently, we only support normalization with a transformation such that the src file contains Affine_0 and Affine_1. Checking for  
103 - * these keys prevents us from pinning eyes on a face that wasn't actually transformed (see AffineTransform).  
104 - * If both cases fail, we default to stasm_search_single. */  
105 -  
106 - if (!pinPoints.isEmpty() && src.file.contains("Affine_0") && src.file.contains("Affine_1")) {  
107 - rightEye = QPointF(pinPoints.at(0), pinPoints.at(1));  
108 - leftEye = QPointF(pinPoints.at(2), pinPoints.at(3));  
109 - searchPinned = true;  
110 - } else if (!pinLabels.isEmpty()) {  
111 - rightEye = src.file.get<QPointF>(pinLabels.at(0), QPointF());  
112 - leftEye = src.file.get<QPointF>(pinLabels.at(1), QPointF());  
113 - searchPinned = true;  
114 - }  
115 -  
116 - if (searchPinned) {  
117 - float pins[2 * stasm_NLANDMARKS];  
118 -  
119 - for (int i = 0; i < nLandmarks; i++) {  
120 - if (i == 38) /* Stasm Right Eye */ { pins[2*i] = rightEye.x(); pins[2*i+1] = rightEye.y(); }  
121 - else if (i == 39) /* Stasm Left Eye */ { pins[2*i] = leftEye.x(); pins[2*i+1] = leftEye.y(); }  
122 - else { pins[2*i] = 0; pins[2*i+1] = 0; }  
123 - } 81 + if (stasm3Format)
  82 + stasm_convert_shape(landmarks, 76);
124 83
125 - stasm_search_pinned(landmarks, pins, reinterpret_cast<const char*>(stasmSrc.data), stasmSrc.cols, stasmSrc.rows, NULL);  
126 -  
127 - // The ASM in Stasm is guaranteed to converge in this case  
128 - foundFace = 1; 84 + QList<QPointF> points;
  85 + for (int i = 0; i < nLandmarks; i++) {
  86 + QPointF point(landmarks[2 * i], landmarks[2 * i + 1]);
  87 + points.append(point);
129 } 88 }
130 89
131 - if (!foundFace) {  
132 - StasmCascadeClassifier *stasmCascade = stasmCascadeResource.acquire();  
133 - stasm_search_single(&foundFace, landmarks, reinterpret_cast<const char*>(stasmSrc.data), stasmSrc.cols, stasmSrc.rows, *stasmCascade, NULL, NULL);  
134 - stasmCascadeResource.release(stasmCascade);  
135 - } 90 + return points;
  91 + }
136 92
137 - if (stasm3Format) {  
138 - nLandmarks = 76;  
139 - stasm_convert_shape(landmarks, nLandmarks);  
140 - } 93 + void project(const Template &src, Template &dst) const
  94 + {
  95 + TemplateList temp;
  96 + project(TemplateList() << src, temp);
  97 + if (!temp.isEmpty()) dst = temp.first();
  98 + }
141 99
142 - // For convenience, if these are the only points/rects we want to deal with as the algorithm progresses  
143 - if (clearLandmarks) {  
144 - dst.file.clearPoints();  
145 - dst.file.clearRects();  
146 - } 100 + void project(const TemplateList &src, TemplateList &dst) const
  101 + {
  102 + foreach (const Template &t, src) {
  103 + Mat stasmSrc(t);
  104 + if (t.m().channels() == 3)
  105 + cvtColor(t, stasmSrc, CV_BGR2GRAY);
  106 + else if (t.m().channels() != 1)
  107 + qFatal("Stasm expects single channel matrices.");
  108 +
  109 + if (!stasmSrc.isContinuous())
  110 + qFatal("Stasm expects continuous matrix data.");
  111 +
  112 + int foundFace = 0;
  113 + int nLandmarks = stasm_NLANDMARKS;
  114 + float landmarks[2 * stasm_NLANDMARKS];
  115 +
  116 + bool searchPinned = false;
  117 +
  118 + QPointF rightEye, leftEye;
  119 + /* Two use cases are accounted for:
  120 + * 1. Pin eyes without normalization: in this case the string list should contain the KEYS for right then left eyes, respectively.
  121 + * 2. Pin eyes with normalization: in this case the string list should contain the COORDINATES of the right then left eyes, respectively.
  122 + * Currently, we only support normalization with a transformation such that the src file contains Affine_0 and Affine_1. Checking for
  123 + * these keys prevents us from pinning eyes on a face that wasn't actually transformed (see AffineTransform).
  124 + * If both cases fail, we default to stasm_search_single. */
  125 +
  126 + if (!pinPoints.isEmpty() && t.file.contains("Affine_0") && t.file.contains("Affine_1")) {
  127 + rightEye = QPointF(pinPoints.at(0), pinPoints.at(1));
  128 + leftEye = QPointF(pinPoints.at(2), pinPoints.at(3));
  129 + searchPinned = true;
  130 + } else if (!pinLabels.isEmpty()) {
  131 + rightEye = t.file.get<QPointF>(pinLabels.at(0), QPointF());
  132 + leftEye = t.file.get<QPointF>(pinLabels.at(1), QPointF());
  133 + searchPinned = true;
  134 + }
  135 +
  136 + if (searchPinned) {
  137 + float pins[2 * stasm_NLANDMARKS];
  138 +
  139 + for (int i = 0; i < nLandmarks; i++) {
  140 + if (i == 38) /* Stasm Right Eye */ { pins[2*i] = rightEye.x(); pins[2*i+1] = rightEye.y(); }
  141 + else if (i == 39) /* Stasm Left Eye */ { pins[2*i] = leftEye.x(); pins[2*i+1] = leftEye.y(); }
  142 + else { pins[2*i] = 0; pins[2*i+1] = 0; }
  143 + }
  144 +
  145 + stasm_search_pinned(landmarks, pins, reinterpret_cast<const char*>(stasmSrc.data), stasmSrc.cols, stasmSrc.rows, NULL);
  146 +
  147 + // The ASM in Stasm is guaranteed to converge in this case
  148 + foundFace = 1;
  149 + Template u(t.file, t.m());
  150 + QList<QPointF> points = convertLandmarks(nLandmarks, landmarks);
  151 + u.file.set("StasmRightEye", points[38]);
  152 + u.file.set("StasmLeftEye", points[39]);
  153 + u.file.appendPoints(points);
  154 + dst.append(u);
  155 + }
147 156
148 - if (!foundFace) {  
149 - if (Globals->verbose) qWarning("No face found in %s.", qPrintable(src.file.fileName()));  
150 - dst.file.fte = true;  
151 - } else {  
152 - QList<QPointF> points;  
153 - for (int i = 0; i < nLandmarks; i++) {  
154 - QPointF point(landmarks[2 * i], landmarks[2 * i + 1]);  
155 - points.append(point); 157 + if (!foundFace) {
  158 + StasmCascadeClassifier *stasmCascade = stasmCascadeResource.acquire();
  159 + foundFace = 1;
  160 + stasm::FaceDet detection;
  161 + while (foundFace) {
  162 + stasm_search_auto(&foundFace, landmarks, reinterpret_cast<const char*>(stasmSrc.data), stasmSrc.cols, stasmSrc.rows, *stasmCascade, detection);
  163 + if (foundFace) {
  164 + Template u(t.file, t.m());
  165 + QList<QPointF> points = convertLandmarks(nLandmarks, landmarks);
  166 + u.file.set("StasmRightEye", points[38]);
  167 + u.file.set("StasmLeftEye", points[39]);
  168 + u.file.appendPoints(points);
  169 + dst.append(u);
  170 + }
  171 +
  172 + if (!Globals->enrollAll)
  173 + break;
  174 + }
  175 + stasmCascadeResource.release(stasmCascade);
156 } 176 }
157 - dst.file.set("StasmRightEye", points[38]);  
158 - dst.file.set("StasmLeftEye", points[39]);  
159 - dst.file.appendPoints(points);  
160 } 177 }
161 } 178 }
162 }; 179 };