Commit 3f3a0a7b3def15208510026e4c53a27b8d13b3cb
1 parent
7693fd5f
Stasm properly uses internal detector to landmark all faces in image
Showing
7 changed files
with
116 additions
and
282 deletions
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
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 | }; | ... | ... |