Commit 1b8a5d53923f4771fe89d373da196d7183c736ac

Authored by Scott Klum
2 parents 7693fd5f b735d779

Merge pull request #435 from biometrics/stasm_multiface

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 47 int minwidth,
48 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 50 CV_Assert(minwidth >= 1 && minwidth <= 100);
61 51  
62   - int minpix = MAX(100, cvRound(img.cols * minwidth / 100.));
63   -
64 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 55 static const int DETECTOR_FLAGS = 0;
68 56  
69 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 61 // copy face rects into the detpars vector
74 62  
... ... @@ -80,8 +68,6 @@ void DetectFaces( // all face rects into detpars
80 68 // detpar.x and detpar.y is the center of the face rectangle
81 69 detpar.x = facerect->x + facerect->width / 2.;
82 70 detpar.y = facerect->y + facerect->height / 2.;
83   - detpar.x -= leftborder; // discount the border we added earlier
84   - detpar.y -= topborder;
85 71 detpar.width = double(facerect->width);
86 72 detpar.height = double(facerect->height);
87 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 173 //CV_Assert(!facedet_g.empty()); // check that OpenFaceDetector_ was called
188 174  
189 175 DetectFaces(detpars_, img, minwidth, cascade);
190   - DiscardMissizedFaces(detpars_);
  176 + //DiscardMissizedFaces(detpars_);
191 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 181 else
196 182 {
... ...
3rdparty/stasm4.0.0/stasm/MOD_1/facedet.h
... ... @@ -38,13 +38,11 @@ public:
38 38  
39 39 FaceDet() {} // constructor
40 40  
41   -
42   -private:
43   - vector<DetPar> detpars_; // all the valid faces in the current image
44   -
45 41 int iface_; // index of current face for NextFace_
46 42 // indexes into detpars_
  43 + vector<DetPar> detpars_; // all the valid faces in the current image
47 44  
  45 +private:
48 46 DISALLOW_COPY_AND_ASSIGN(FaceDet);
49 47  
50 48 }; // end class FaceDet
... ...
3rdparty/stasm4.0.0/stasm/stasm_lib.cpp
... ... @@ -160,7 +160,8 @@ int stasm_search_auto_ext( // extended version of stasm_search_auto
160 160 const char* data,
161 161 const int width,
162 162 const int height,
163   - StasmCascadeClassifier cascade)
  163 + StasmCascadeClassifier cascade,
  164 + FaceDet &detection)
164 165 {
165 166 int returnval = 1; // assume success
166 167 *foundface = 0; // but assume no face found
... ... @@ -176,15 +177,14 @@ int stasm_search_auto_ext( // extended version of stasm_search_auto
176 177 // Allocate image
177 178 Image img = Image(height, width,(unsigned char*)data);
178 179  
179   - FaceDet facedet;
180   -
181 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 184 // Get the start shape for the next face in the image, and the ROI around it.
185 185 // The shape will be wrt the ROI frame.
186 186 if (NextStartShapeAndRoi(shape, face_roi, detpar_roi, detpar,
187   - img, mods_g, facedet, cascade))
  187 + img, mods_g, detection, cascade))
188 188 {
189 189 // now working with maybe flipped ROI and start shape in ROI frame
190 190 *foundface = 1;
... ... @@ -219,9 +219,10 @@ int stasm_search_auto(// call repeatedly to find all faces
219 219 const char *data,
220 220 const int width,
221 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 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 238 (void) datadir;
238 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 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 64  
65 65 #include "stasmcascadeclassifier.h"
66 66  
  67 +namespace stasm {
  68 + class FaceDet;
  69 +}
  70 +
67 71 static const int stasm_NLANDMARKS = 77; // number of landmarks
68 72  
69 73 extern const char* const stasm_VERSION;
... ... @@ -89,7 +93,8 @@ int stasm_search_auto( // call repeatedly to find all faces
89 93 const char* data,
90 94 const int width,
91 95 const int height,
92   - StasmCascadeClassifier cascade);
  96 + StasmCascadeClassifier cascade,
  97 + stasm::FaceDet &detection);
93 98  
94 99 extern "C"
95 100 int stasm_search_single( // wrapper for stasm_search_auto and friends
... ...
openbr/plugins/core/algorithms.cpp
... ... @@ -39,7 +39,7 @@ class AlgorithmsInitializer : public Initializer
39 39 Globals->abbreviations.insert("FR_Mouth", "(CropFromLandmarks([59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76])+Resize(24,48))");
40 40 Globals->abbreviations.insert("FR_Nose", "(CropFromLandmarks([16,17,18,19,20,21,22,23,24,25,26,27],padding=3)+Resize(36,36))");
41 41 Globals->abbreviations.insert("FR_Face", "(Crop(24,24,88,88)+Resize(44,44))");
42   - Globals->abbreviations.insert("FR_Detect", "(FaceDetection+Stasm+Rename(StasmLeftEye,Affine_1,true)+Rename(StasmRightEye,Affine_0,true)+Affine(136,136,0.35,0.35,warpPoints=true))");
  42 + Globals->abbreviations.insert("FR_Detect", "(Open+Cvt(Gray)+Cascade+Stasm+Rename(StasmLeftEye,Affine_1,true)+Rename(StasmRightEye,Affine_0,true)+Affine(136,136,0.35,0.35,warpPoints=true))");
43 43 Globals->abbreviations.insert("FR_Represent", "((DenseHOG/DenseLBP)+Cat+LDA(.98)+Normalize(L2))");
44 44  
45 45 Globals->abbreviations.insert("GenderClassification", "FaceDetection+Expand+FaceClassificationRegistration+Expand+<FaceClassificationExtraction>+<GenderClassifier>+Discard");
... ...
openbr/plugins/metadata/stasm4.cpp
1 1 #include <QString>
2   -#include <stasm_lib.h>
3 2 #include <stasmcascadeclassifier.h>
  3 +#include <stasm_lib.h>
  4 +#include <stasm.h>
4 5 #include <opencv2/opencv.hpp>
5 6  
6 7 #include <openbr/plugins/openbr_internal.h>
... ... @@ -56,18 +57,18 @@ BR_REGISTER(Initializer, StasmInitializer)
56 57 * \brief Wraps STASM key point detector
57 58 * \author Scott Klum \cite sklum
58 59 */
59   -class StasmTransform : public UntrainableTransform
  60 +class StasmTransform : public UntrainableMetaTransform
60 61 {
61 62 Q_OBJECT
62 63  
63 64 Q_PROPERTY(bool stasm3Format READ get_stasm3Format WRITE set_stasm3Format RESET reset_stasm3Format STORED false)
64 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 66 Q_PROPERTY(QList<float> pinPoints READ get_pinPoints WRITE set_pinPoints RESET reset_pinPoints STORED false)
68 67 BR_PROPERTY(QList<float>, pinPoints, QList<float>())
69 68 Q_PROPERTY(QStringList pinLabels READ get_pinLabels WRITE set_pinLabels RESET reset_pinLabels STORED false)
70 69 BR_PROPERTY(QStringList, pinLabels, QStringList())
  70 + Q_PROPERTY(QString inputVariable READ get_inputVariable WRITE set_inputVariable RESET reset_inputVariable STORED false)
  71 + BR_PROPERTY(QString, inputVariable, "FrontalFace")
71 72  
72 73 Resource<StasmCascadeClassifier> stasmCascadeResource;
73 74  
... ... @@ -77,86 +78,119 @@ class StasmTransform : public UntrainableTransform
77 78 stasmCascadeResource.setResourceMaker(new StasmResourceMaker());
78 79 }
79 80  
80   - void project(const Template &src, Template &dst) const
  81 + QList<QPointF> convertLandmarks(int nLandmarks, float *landmarks) const
81 82 {
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   - }
  83 + if (stasm3Format)
  84 + stasm_convert_shape(landmarks, 76);
124 85  
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;
  86 + QList<QPointF> points;
  87 + for (int i = 0; i < nLandmarks; i++) {
  88 + QPointF point(landmarks[2 * i], landmarks[2 * i + 1]);
  89 + points.append(point);
129 90 }
130 91  
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   - }
  92 + return points;
  93 + }
136 94  
137   - if (stasm3Format) {
138   - nLandmarks = 76;
139   - stasm_convert_shape(landmarks, nLandmarks);
140   - }
  95 + void project(const Template &src, Template &dst) const
  96 + {
  97 + TemplateList temp;
  98 + project(TemplateList() << src, temp);
  99 + if (!temp.isEmpty()) dst = temp.first();
  100 + }
141 101  
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   - }
  102 + void project(const TemplateList &src, TemplateList &dst) const
  103 + {
  104 + foreach (const Template &t, src) {
  105 + Mat stasmSrc(t);
  106 + if (t.m().channels() == 3)
  107 + cvtColor(t, stasmSrc, CV_BGR2GRAY);
  108 + else if (t.m().channels() != 1)
  109 + qFatal("Stasm expects single channel matrices.");
  110 +
  111 + if (!stasmSrc.isContinuous())
  112 + qFatal("Stasm expects continuous matrix data.");
  113 +
  114 + int foundFace = 0;
  115 + int nLandmarks = stasm_NLANDMARKS;
  116 + float landmarks[2 * stasm_NLANDMARKS];
  117 +
  118 + bool searchPinned = false;
  119 +
  120 + QPointF rightEye, leftEye;
  121 + /* Two use cases are accounted for:
  122 + * 1. Pin eyes without normalization: in this case the string list should contain the KEYS for right then left eyes, respectively.
  123 + * 2. Pin eyes with normalization: in this case the string list should contain the COORDINATES of the right then left eyes, respectively.
  124 + * Currently, we only support normalization with a transformation such that the src file contains Affine_0 and Affine_1. Checking for
  125 + * these keys prevents us from pinning eyes on a face that wasn't actually transformed (see AffineTransform).
  126 + * If both cases fail, we default to stasm_search_single. */
  127 +
  128 + if (!pinPoints.isEmpty() && t.file.contains("Affine_0") && t.file.contains("Affine_1")) {
  129 + rightEye = QPointF(pinPoints.at(0), pinPoints.at(1));
  130 + leftEye = QPointF(pinPoints.at(2), pinPoints.at(3));
  131 + searchPinned = true;
  132 + } else if (!pinLabels.isEmpty()) {
  133 + rightEye = t.file.get<QPointF>(pinLabels.at(0), QPointF());
  134 + leftEye = t.file.get<QPointF>(pinLabels.at(1), QPointF());
  135 + searchPinned = true;
  136 + }
  137 +
  138 + if (searchPinned) {
  139 + float pins[2 * stasm_NLANDMARKS];
  140 +
  141 + for (int i = 0; i < nLandmarks; i++) {
  142 + if (i == 38) /* Stasm Right Eye */ { pins[2*i] = rightEye.x(); pins[2*i+1] = rightEye.y(); }
  143 + else if (i == 39) /* Stasm Left Eye */ { pins[2*i] = leftEye.x(); pins[2*i+1] = leftEye.y(); }
  144 + else { pins[2*i] = 0; pins[2*i+1] = 0; }
  145 + }
  146 +
  147 + stasm_search_pinned(landmarks, pins, reinterpret_cast<const char*>(stasmSrc.data), stasmSrc.cols, stasmSrc.rows, NULL);
  148 +
  149 + // The ASM in Stasm is guaranteed to converge in this case
  150 + foundFace = 1;
  151 + Template u(t.file, t.m());
  152 + QList<QPointF> points = convertLandmarks(nLandmarks, landmarks);
  153 + u.file.set("StasmRightEye", points[38]);
  154 + u.file.set("StasmLeftEye", points[39]);
  155 + u.file.appendPoints(points);
  156 + dst.append(u);
  157 + }
147 158  
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);
  159 + if (!foundFace) {
  160 + StasmCascadeClassifier *stasmCascade = stasmCascadeResource.acquire();
  161 + foundFace = 1;
  162 + stasm::FaceDet detection;
  163 +
  164 + QList<QRectF> rects = t.file.contains(inputVariable) ? QList<QRectF>() << t.file.get<QRectF>(inputVariable) : t.file.rects();
  165 + if (!rects.isEmpty()) detection.iface_ = 0;
  166 + for (int i=0; i<rects.size(); i++) {
  167 + Rect rect = OpenCVUtils::toRect(rects[i]);
  168 + stasm::DetPar detpar;
  169 + detpar.x = rect.x + rect.width / 2.;
  170 + detpar.y = rect.y + rect.height / 2.;
  171 + detpar.width = double(rect.width);
  172 + detpar.height = double(rect.height);
  173 + detpar.yaw = 0;
  174 + detpar.eyaw = stasm::EYAW00;
  175 + detection.detpars_.push_back(detpar);
  176 + }
  177 +
  178 + while (foundFace) {
  179 + stasm_search_auto(&foundFace, landmarks, reinterpret_cast<const char*>(stasmSrc.data), stasmSrc.cols, stasmSrc.rows, *stasmCascade, detection);
  180 + if (foundFace) {
  181 + Template u(t.file, t.m());
  182 + QList<QPointF> points = convertLandmarks(nLandmarks, landmarks);
  183 + u.file.set("StasmRightEye", points[38]);
  184 + u.file.set("StasmLeftEye", points[39]);
  185 + u.file.appendPoints(points);
  186 + dst.append(u);
  187 + }
  188 +
  189 + if (!Globals->enrollAll)
  190 + break;
  191 + }
  192 + stasmCascadeResource.release(stasmCascade);
156 193 }
157   - dst.file.set("StasmRightEye", points[38]);
158   - dst.file.set("StasmLeftEye", points[39]);
159   - dst.file.appendPoints(points);
160 194 }
161 195 }
162 196 };
... ...