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 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
... ... @@ -39,9 +39,9 @@ public:
39 39 FaceDet() {} // constructor
40 40  
41 41  
42   -private:
43 42 vector<DetPar> detpars_; // all the valid faces in the current image
44 43  
  44 +private:
45 45 int iface_; // index of current face for NextFace_
46 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 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/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,14 +57,12 @@ 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)
... ... @@ -77,86 +76,104 @@ class StasmTransform : public UntrainableTransform
77 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 };
... ...