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,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 | }; |