Commit 6b5c39957ab3caf12c9b7c0aa6594f843ef1de9d
Merge branch 'master' of https://github.com/biometrics/openbr
Showing
48 changed files
with
635 additions
and
560 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
| @@ -38,13 +38,11 @@ public: | @@ -38,13 +38,11 @@ public: | ||
| 38 | 38 | ||
| 39 | FaceDet() {} // constructor | 39 | FaceDet() {} // constructor |
| 40 | 40 | ||
| 41 | - | ||
| 42 | -private: | ||
| 43 | - vector<DetPar> detpars_; // all the valid faces in the current image | ||
| 44 | - | ||
| 45 | int iface_; // index of current face for NextFace_ | 41 | int iface_; // index of current face for NextFace_ |
| 46 | // indexes into detpars_ | 42 | // indexes into detpars_ |
| 43 | + vector<DetPar> detpars_; // all the valid faces in the current image | ||
| 47 | 44 | ||
| 45 | +private: | ||
| 48 | DISALLOW_COPY_AND_ASSIGN(FaceDet); | 46 | DISALLOW_COPY_AND_ASSIGN(FaceDet); |
| 49 | 47 | ||
| 50 | }; // end class FaceDet | 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,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 |
CMakeLists.txt
| @@ -13,7 +13,7 @@ if(NOT DEFINED CPACK_PACKAGE_VERSION_MAJOR) | @@ -13,7 +13,7 @@ if(NOT DEFINED CPACK_PACKAGE_VERSION_MAJOR) | ||
| 13 | set(CPACK_PACKAGE_VERSION_MAJOR 1) | 13 | set(CPACK_PACKAGE_VERSION_MAJOR 1) |
| 14 | endif() | 14 | endif() |
| 15 | if(NOT DEFINED CPACK_PACKAGE_VERSION_MINOR) | 15 | if(NOT DEFINED CPACK_PACKAGE_VERSION_MINOR) |
| 16 | - set(CPACK_PACKAGE_VERSION_MINOR 0) | 16 | + set(CPACK_PACKAGE_VERSION_MINOR 1) |
| 17 | endif() | 17 | endif() |
| 18 | if(NOT DEFINED CPACK_PACKAGE_VERSION_PATCH) | 18 | if(NOT DEFINED CPACK_PACKAGE_VERSION_PATCH) |
| 19 | set(CPACK_PACKAGE_VERSION_PATCH 0) | 19 | set(CPACK_PACKAGE_VERSION_PATCH 0) |
| @@ -158,8 +158,8 @@ endif() | @@ -158,8 +158,8 @@ endif() | ||
| 158 | 158 | ||
| 159 | # Download the models | 159 | # Download the models |
| 160 | ExternalProject_Add(models | 160 | ExternalProject_Add(models |
| 161 | - URL http://github.com/biometrics/openbr/releases/download/v1.0.0/models.tar.gz | ||
| 162 | - URL_MD5 0a7c79226d6629954aa32c835a1007b9 | 161 | + URL http://github.com/biometrics/openbr/releases/download/v1.1.0/models.tar.gz |
| 162 | + URL_MD5 26cf71abd48cec8d7b2abf3e8f4ebfc4 | ||
| 163 | SOURCE_DIR "${PROJECT_SOURCE_DIR}/share/openbr/models" | 163 | SOURCE_DIR "${PROJECT_SOURCE_DIR}/share/openbr/models" |
| 164 | CONFIGURE_COMMAND "" | 164 | CONFIGURE_COMMAND "" |
| 165 | BUILD_COMMAND "" | 165 | BUILD_COMMAND "" |
app/br/br.cpp
| @@ -169,8 +169,8 @@ public: | @@ -169,8 +169,8 @@ public: | ||
| 169 | check(parc >= 2 && parc <= 4, "Incorrect parameter count for 'evalRegression'."); | 169 | check(parc >= 2 && parc <= 4, "Incorrect parameter count for 'evalRegression'."); |
| 170 | br_eval_regression(parv[0], parv[1], parc >= 3 ? parv[2] : "", parc >= 4 ? parv[3] : ""); | 170 | br_eval_regression(parv[0], parv[1], parc >= 3 ? parv[2] : "", parc >= 4 ? parv[3] : ""); |
| 171 | } else if (!strcmp(fun, "evalKNN")) { | 171 | } else if (!strcmp(fun, "evalKNN")) { |
| 172 | - check(parc == 3, "Incorrect parameter count for 'evalKNN'."); | ||
| 173 | - br_eval_knn(parv[0], parv[1], parv[2]); | 172 | + check(parc >= 2 && parc <= 3, "Incorrect parameter count for 'evalKNN'."); |
| 173 | + br_eval_knn(parv[0], parv[1], parc > 2 ? parv[2] : ""); | ||
| 174 | } else if (!strcmp(fun, "pairwiseCompare")) { | 174 | } else if (!strcmp(fun, "pairwiseCompare")) { |
| 175 | check((parc >= 2) && (parc <= 3), "Incorrect parameter count for 'pairwiseCompare'."); | 175 | check((parc >= 2) && (parc <= 3), "Incorrect parameter count for 'pairwiseCompare'."); |
| 176 | br_pairwise_compare(parv[0], parv[1], parc == 3 ? parv[2] : ""); | 176 | br_pairwise_compare(parv[0], parv[1], parc == 3 ? parv[2] : ""); |
| @@ -186,6 +186,9 @@ public: | @@ -186,6 +186,9 @@ public: | ||
| 186 | } else if (!strcmp(fun, "plotMetadata")) { | 186 | } else if (!strcmp(fun, "plotMetadata")) { |
| 187 | check(parc >= 2, "Incorrect parameter count for 'plotMetadata'."); | 187 | check(parc >= 2, "Incorrect parameter count for 'plotMetadata'."); |
| 188 | br_plot_metadata(parc-1, parv, parv[parc-1], true); | 188 | br_plot_metadata(parc-1, parv, parv[parc-1], true); |
| 189 | + } else if (!strcmp(fun, "plotKNN")) { | ||
| 190 | + check(parc >=2, "Incorrect parameter count for 'plotKNN'."); | ||
| 191 | + br_plot_knn(parc-1, parv, parv[parc-1], true); | ||
| 189 | } else if (!strcmp(fun, "project")) { | 192 | } else if (!strcmp(fun, "project")) { |
| 190 | check(parc == 2, "Insufficient parameter count for 'project'."); | 193 | check(parc == 2, "Insufficient parameter count for 'project'."); |
| 191 | br_project(parv[0], parv[1]); | 194 | br_project(parv[0], parv[1]); |
| @@ -279,13 +282,14 @@ private: | @@ -279,13 +282,14 @@ private: | ||
| 279 | "-evalDetection <predicted_gallery> <truth_gallery> [{csv}] [{normalize}] [{minSize}] [{maxSize}]\n" | 282 | "-evalDetection <predicted_gallery> <truth_gallery> [{csv}] [{normalize}] [{minSize}] [{maxSize}]\n" |
| 280 | "-evalLandmarking <predicted_gallery> <truth_gallery> [{csv} [<normalization_index_a> <normalization_index_b>] [sample_index] [total_examples]]\n" | 283 | "-evalLandmarking <predicted_gallery> <truth_gallery> [{csv} [<normalization_index_a> <normalization_index_b>] [sample_index] [total_examples]]\n" |
| 281 | "-evalRegression <predicted_gallery> <truth_gallery> <predicted property name> <ground truth property name>\n" | 284 | "-evalRegression <predicted_gallery> <truth_gallery> <predicted property name> <ground truth property name>\n" |
| 282 | - "-evalKNN <knn_graph> <knn_truth> [{iet_file}]\n" | 285 | + "-evalKNN <knn_graph> <knn_truth> [{csv}]\n" |
| 283 | "-pairwiseCompare <target_gallery> <query_gallery> [{output}]\n" | 286 | "-pairwiseCompare <target_gallery> <query_gallery> [{output}]\n" |
| 284 | "-inplaceEval <simmat> <target> <query> [{csv}]\n" | 287 | "-inplaceEval <simmat> <target> <query> [{csv}]\n" |
| 285 | "-assertEval <simmat> <mask> <accuracy>\n" | 288 | "-assertEval <simmat> <mask> <accuracy>\n" |
| 286 | "-plotDetection <file> ... <file> {destination}\n" | 289 | "-plotDetection <file> ... <file> {destination}\n" |
| 287 | "-plotLandmarking <file> ... <file> {destination}\n" | 290 | "-plotLandmarking <file> ... <file> {destination}\n" |
| 288 | "-plotMetadata <file> ... <file> <columns>\n" | 291 | "-plotMetadata <file> ... <file> <columns>\n" |
| 292 | + "-plotKNN <file> ... <file> {destination}\n" | ||
| 289 | "-project <input_gallery> {output_gallery}\n" | 293 | "-project <input_gallery> {output_gallery}\n" |
| 290 | "-deduplicate <input_gallery> <output_gallery> <threshold>\n" | 294 | "-deduplicate <input_gallery> <output_gallery> <threshold>\n" |
| 291 | "-likely <input_type> <output_type> <output_likely_source>\n" | 295 | "-likely <input_type> <output_type> <output_likely_source>\n" |
docs/docs/api_docs/cl_api.md
| @@ -59,6 +59,17 @@ DOCUMENT ME | @@ -59,6 +59,17 @@ DOCUMENT ME | ||
| 59 | 59 | ||
| 60 | * **wraps:** [br_pairwise_compare](c_api/functions.md#br_pairwise_compare) | 60 | * **wraps:** [br_pairwise_compare](c_api/functions.md#br_pairwise_compare) |
| 61 | 61 | ||
| 62 | +### -crossValidate {: #crossvalidate } | ||
| 63 | + | ||
| 64 | +Either performs n fold cross validation (if nFolds > 0), or a single iteration of a train / test split with the first of the n folds as the test partition (nFolds < 0). | ||
| 65 | + | ||
| 66 | +* **arguments:** | ||
| 67 | + | ||
| 68 | + -crossValidate <nFolds> -algorithm "CrossValidate(description=$ALG,randomSeed=5):CrossValidate+Dist" -train <inputFile> <model> | ||
| 69 | + | ||
| 70 | + -crossValidate <nFolds> -algorithm <model> -enroll <inputFile> input.gal -compare input.gal . out.eval | ||
| 71 | + | ||
| 72 | + | ||
| 62 | ### -eval {: #eval } | 73 | ### -eval {: #eval } |
| 63 | 74 | ||
| 64 | Evaluate a similarity matrix | 75 | Evaluate a similarity matrix |
docs/docs/install.md
| @@ -35,7 +35,7 @@ A hacker's guide to building, editing, and running OpenBR. | @@ -35,7 +35,7 @@ A hacker's guide to building, editing, and running OpenBR. | ||
| 35 | 35 | ||
| 36 | $ git clone https://github.com/biometrics/openbr.git | 36 | $ git clone https://github.com/biometrics/openbr.git |
| 37 | $ cd openbr | 37 | $ cd openbr |
| 38 | - $ git checkout 1.0 | 38 | + $ git checkout v1.1.0 |
| 39 | $ git submodule init | 39 | $ git submodule init |
| 40 | $ git submodule update | 40 | $ git submodule update |
| 41 | 41 | ||
| @@ -116,7 +116,7 @@ A hacker's guide to building, editing, and running OpenBR. | @@ -116,7 +116,7 @@ A hacker's guide to building, editing, and running OpenBR. | ||
| 116 | 116 | ||
| 117 | $ git clone https://github.com/biometrics/openbr.git | 117 | $ git clone https://github.com/biometrics/openbr.git |
| 118 | $ cd openbr | 118 | $ cd openbr |
| 119 | - $ git checkout 1.0 | 119 | + $ git checkout v1.1.0 |
| 120 | $ git submodule init | 120 | $ git submodule init |
| 121 | $ git submodule update | 121 | $ git submodule update |
| 122 | 122 | ||
| @@ -196,7 +196,7 @@ A hacker's guide to building, editing, and running OpenBR. | @@ -196,7 +196,7 @@ A hacker's guide to building, editing, and running OpenBR. | ||
| 196 | $ cd /c | 196 | $ cd /c |
| 197 | $ git clone https://github.com/biometrics/openbr.git | 197 | $ git clone https://github.com/biometrics/openbr.git |
| 198 | $ cd openbr | 198 | $ cd openbr |
| 199 | - $ git checkout 1.0 | 199 | + $ git checkout v1.1.0 |
| 200 | $ git submodule init | 200 | $ git submodule init |
| 201 | $ git submodule update | 201 | $ git submodule update |
| 202 | 202 | ||
| @@ -277,7 +277,7 @@ A hacker's guide to building, editing, and running OpenBR. | @@ -277,7 +277,7 @@ A hacker's guide to building, editing, and running OpenBR. | ||
| 277 | 277 | ||
| 278 | $ git clone https://github.com/biometrics/openbr.git | 278 | $ git clone https://github.com/biometrics/openbr.git |
| 279 | $ cd openbr | 279 | $ cd openbr |
| 280 | - $ git checkout 1.0 | 280 | + $ git checkout v1.1.0 |
| 281 | $ git submodule init | 281 | $ git submodule init |
| 282 | $ git submodule update | 282 | $ git submodule update |
| 283 | 283 |
openbr/core/boost.cpp
| @@ -131,23 +131,16 @@ static CvMat* cvPreprocessIndexArray( const CvMat* idx_arr, int data_arr_size, b | @@ -131,23 +131,16 @@ static CvMat* cvPreprocessIndexArray( const CvMat* idx_arr, int data_arr_size, b | ||
| 131 | 131 | ||
| 132 | //------------------------------------- FeatureEvaluator --------------------------------------- | 132 | //------------------------------------- FeatureEvaluator --------------------------------------- |
| 133 | 133 | ||
| 134 | -void FeatureEvaluator::init(Representation *_representation, int _maxSampleCount, int channels) | 134 | +void FeatureEvaluator::init(Representation *_representation, int _maxSampleCount) |
| 135 | { | 135 | { |
| 136 | representation = _representation; | 136 | representation = _representation; |
| 137 | - | ||
| 138 | - int dx, dy; | ||
| 139 | - Size windowSize = representation->windowSize(&dx, &dy); | ||
| 140 | - data.create((int)_maxSampleCount, (windowSize.width + dx) * (windowSize.height + dy), CV_32SC(channels)); | ||
| 141 | cls.create( (int)_maxSampleCount, 1, CV_32FC1 ); | 137 | cls.create( (int)_maxSampleCount, 1, CV_32FC1 ); |
| 142 | } | 138 | } |
| 143 | 139 | ||
| 144 | -void FeatureEvaluator::setImage(const Mat &img, uchar clsLabel, int idx) | 140 | +void FeatureEvaluator::setImage(const Template &src, uchar clsLabel, int idx) |
| 145 | { | 141 | { |
| 146 | cls.ptr<float>(idx)[0] = clsLabel; | 142 | cls.ptr<float>(idx)[0] = clsLabel; |
| 147 | - | ||
| 148 | - Mat pp; | ||
| 149 | - representation->preprocess(img, pp); | ||
| 150 | - pp.reshape(0, 1).copyTo(data.row(idx)); | 143 | + data.append(representation->preprocess(src)); |
| 151 | } | 144 | } |
| 152 | 145 | ||
| 153 | //----------------------------- CascadeBoostParams ------------------------------------------------- | 146 | //----------------------------- CascadeBoostParams ------------------------------------------------- |
openbr/core/boost.h
| @@ -2,7 +2,6 @@ | @@ -2,7 +2,6 @@ | ||
| 2 | #define _BOOST_H_ | 2 | #define _BOOST_H_ |
| 3 | 3 | ||
| 4 | #include "ml.h" | 4 | #include "ml.h" |
| 5 | -#include <time.h> | ||
| 6 | #include <openbr/openbr_plugin.h> | 5 | #include <openbr/openbr_plugin.h> |
| 7 | 6 | ||
| 8 | #ifdef _WIN32 | 7 | #ifdef _WIN32 |
| @@ -17,9 +16,9 @@ namespace br | @@ -17,9 +16,9 @@ namespace br | ||
| 17 | struct FeatureEvaluator | 16 | struct FeatureEvaluator |
| 18 | { | 17 | { |
| 19 | ~FeatureEvaluator() {} | 18 | ~FeatureEvaluator() {} |
| 20 | - void init(Representation *_representation, int _maxSampleCount, int channels); | ||
| 21 | - void setImage(const cv::Mat& img, uchar clsLabel, int idx); | ||
| 22 | - float operator()(int featureIdx, int sampleIdx) const { return representation->evaluate(data.row(sampleIdx), featureIdx); } | 19 | + void init(Representation *_representation, int _maxSampleCount); |
| 20 | + void setImage(const Template &src, uchar clsLabel, int idx); | ||
| 21 | + float operator()(int featureIdx, int sampleIdx) const { return representation->evaluate(data[sampleIdx], featureIdx); } | ||
| 23 | 22 | ||
| 24 | int getNumFeatures() const { return representation->numFeatures(); } | 23 | int getNumFeatures() const { return representation->numFeatures(); } |
| 25 | int getMaxCatCount() const { return representation->maxCatCount(); } | 24 | int getMaxCatCount() const { return representation->maxCatCount(); } |
| @@ -27,7 +26,8 @@ struct FeatureEvaluator | @@ -27,7 +26,8 @@ struct FeatureEvaluator | ||
| 27 | const cv::Mat& getCls() const { return cls; } | 26 | const cv::Mat& getCls() const { return cls; } |
| 28 | float getCls(int si) const { return cls.at<float>(si, 0); } | 27 | float getCls(int si) const { return cls.at<float>(si, 0); } |
| 29 | 28 | ||
| 30 | - cv::Mat data, cls; | 29 | + cv::Mat cls; |
| 30 | + TemplateList data; | ||
| 31 | Representation *representation; | 31 | Representation *representation; |
| 32 | }; | 32 | }; |
| 33 | 33 |
openbr/core/eval.cpp
| @@ -825,16 +825,25 @@ static QStringList computeDetectionResults(const QList<ResolvedDetection> &detec | @@ -825,16 +825,25 @@ static QStringList computeDetectionResults(const QList<ResolvedDetection> &detec | ||
| 825 | if (prevFP / numImages < 0.1 && FP / numImages > 0.1 && discrete) { | 825 | if (prevFP / numImages < 0.1 && FP / numImages > 0.1 && discrete) { |
| 826 | qDebug("TAR @ FAR => %f : 0.1", TP / totalTrueDetections); | 826 | qDebug("TAR @ FAR => %f : 0.1", TP / totalTrueDetections); |
| 827 | qDebug("Confidence: %f", detection.confidence); | 827 | qDebug("Confidence: %f", detection.confidence); |
| 828 | + qDebug("TP vs. FP: %f to %f", TP, FP); | ||
| 828 | } else if (prevFP / numImages < 0.01 && FP / numImages > 0.01 && discrete) { | 829 | } else if (prevFP / numImages < 0.01 && FP / numImages > 0.01 && discrete) { |
| 829 | qDebug("TAR @ FAR => %f : 0.01", TP / totalTrueDetections); | 830 | qDebug("TAR @ FAR => %f : 0.01", TP / totalTrueDetections); |
| 830 | qDebug("Confidence: %f", detection.confidence); | 831 | qDebug("Confidence: %f", detection.confidence); |
| 832 | + qDebug("TP vs. FP: %f to %f", TP, FP); | ||
| 833 | + } else if (prevFP / numImages < 0.001 && FP / numImages > 0.001 && discrete) { | ||
| 834 | + qDebug("TAR @ FAR => %f : 0.001", TP / totalTrueDetections); | ||
| 835 | + qDebug("Confidence: %f", detection.confidence); | ||
| 836 | + qDebug("TP vs. FP: %f to %f", TP, FP); | ||
| 831 | } | 837 | } |
| 838 | + | ||
| 832 | points.append(DetectionOperatingPoint(TP, FP, totalTrueDetections, numImages)); | 839 | points.append(DetectionOperatingPoint(TP, FP, totalTrueDetections, numImages)); |
| 833 | prevFP = FP; | 840 | prevFP = FP; |
| 834 | } | 841 | } |
| 835 | } | 842 | } |
| 836 | } | 843 | } |
| 837 | 844 | ||
| 845 | + if (discrete) qDebug("Total TP vs. FP: %f to %f", TP, FP); | ||
| 846 | + | ||
| 838 | const int keep = qMin(points.size(), Max_Points); | 847 | const int keep = qMin(points.size(), Max_Points); |
| 839 | if (keep < 1) qFatal("Insufficient points."); | 848 | if (keep < 1) qFatal("Insufficient points."); |
| 840 | 849 | ||
| @@ -1336,7 +1345,7 @@ void readKNNTruth(size_t probeCount, QVector< QList<size_t> > &groundTruth, cons | @@ -1336,7 +1345,7 @@ void readKNNTruth(size_t probeCount, QVector< QList<size_t> > &groundTruth, cons | ||
| 1336 | qFatal("Invalid ground truth file!"); | 1345 | qFatal("Invalid ground truth file!"); |
| 1337 | } | 1346 | } |
| 1338 | 1347 | ||
| 1339 | -void EvalKNN(const QString &knnGraph, const QString &knnTruth, const QString &iet) | 1348 | +void EvalKNN(const QString &knnGraph, const QString &knnTruth, const QString &csv) |
| 1340 | { | 1349 | { |
| 1341 | qDebug("Evaluating k-NN of %s against %s", qPrintable(knnGraph), qPrintable(knnTruth)); | 1350 | qDebug("Evaluating k-NN of %s against %s", qPrintable(knnGraph), qPrintable(knnTruth)); |
| 1342 | 1351 | ||
| @@ -1406,19 +1415,14 @@ void EvalKNN(const QString &knnGraph, const QString &knnTruth, const QString &ie | @@ -1406,19 +1415,14 @@ void EvalKNN(const QString &knnGraph, const QString &knnTruth, const QString &ie | ||
| 1406 | if (numUnmatedSearches == 0) | 1415 | if (numUnmatedSearches == 0) |
| 1407 | qFatal("No unmated searches!"); | 1416 | qFatal("No unmated searches!"); |
| 1408 | 1417 | ||
| 1409 | - printf("Rank-%i Return Rate: %g\n", 1, getCMC(firstGenuineReturns, 1, numMatedSearches)); | 1418 | + |
| 1419 | + qDebug("Rank-%d Return Rate: %.3f", 1, getCMC(firstGenuineReturns, 1, numMatedSearches)); | ||
| 1410 | if (k >=5) | 1420 | if (k >=5) |
| 1411 | - printf("Rank-%i Return Rate: %g\n", 5, getCMC(firstGenuineReturns, 5, numMatedSearches)); | 1421 | + qDebug("Rank-%d Return Rate: %.3f", 5, getCMC(firstGenuineReturns, 5, numMatedSearches)); |
| 1412 | if (k >=10) | 1422 | if (k >=10) |
| 1413 | - printf("Rank-%i Return Rate: %g\n", 10, getCMC(firstGenuineReturns, 10, numMatedSearches)); | ||
| 1414 | - | ||
| 1415 | - printf("Rank-%zu Return Rate: %g\n", k, double(numMatedSimilarities) / double(numMatedSearches)); | 1423 | + qDebug("Rank-%d Return Rate: %.3f", 10, getCMC(firstGenuineReturns, 10, numMatedSearches)); |
| 1416 | 1424 | ||
| 1417 | - // Open the output file | ||
| 1418 | - QFile ietFile(iet); | ||
| 1419 | - if (!ietFile.open(QFile::WriteOnly | QFile::Text)) | ||
| 1420 | - qFatal("Failed to open IET file for writing!"); | ||
| 1421 | - ietFile.write("Threshold,FPIR,FNIR\n"); | 1425 | + qDebug("Rank-%zu Return Rate: %.3f", k, double(numMatedSimilarities) / double(numMatedSearches)); |
| 1422 | 1426 | ||
| 1423 | /* | 1427 | /* |
| 1424 | * Iterate through the similarity scores highest-to-lowest, | 1428 | * Iterate through the similarity scores highest-to-lowest, |
| @@ -1447,10 +1451,28 @@ void EvalKNN(const QString &knnGraph, const QString &knnTruth, const QString &ie | @@ -1447,10 +1451,28 @@ void EvalKNN(const QString &knnGraph, const QString &knnTruth, const QString &ie | ||
| 1447 | } | 1451 | } |
| 1448 | } | 1452 | } |
| 1449 | 1453 | ||
| 1450 | - foreach(const OperatingPoint &operatingPoint, operatingPoints) | ||
| 1451 | - ietFile.write(qPrintable(QString::number(operatingPoint.score) + "," + | ||
| 1452 | - QString::number(operatingPoint.FAR) + "," + | ||
| 1453 | - QString::number(operatingPoint.TAR) + "\n")); | 1454 | + if (!csv.isEmpty()) { |
| 1455 | + // Open the output file | ||
| 1456 | + QFile ietFile(csv); | ||
| 1457 | + if (!ietFile.open(QFile::WriteOnly | QFile::Text)) | ||
| 1458 | + qFatal("Failed to open IET file for writing!"); | ||
| 1459 | + ietFile.write("Plot,X,Y,Z\n"); | ||
| 1460 | + // Write CMC | ||
| 1461 | + const int Max_Retrieval = min(200, (int)k); | ||
| 1462 | + for (int i=1; i<=Max_Retrieval; i++) { | ||
| 1463 | + const float retrievalRate = getCMC(firstGenuineReturns, i, numMatedSearches); | ||
| 1464 | + ietFile.write(qPrintable(QString("CMC,%1,%2,0\n").arg(QString::number(i), QString::number(retrievalRate)))); | ||
| 1465 | + } | ||
| 1466 | + | ||
| 1467 | + foreach(const OperatingPoint &operatingPoint, operatingPoints) | ||
| 1468 | + ietFile.write(qPrintable("IET," + | ||
| 1469 | + QString::number(operatingPoint.FAR) + "," + | ||
| 1470 | + QString::number(operatingPoint.TAR) + "," + | ||
| 1471 | + QString::number(operatingPoint.score) + "\n")); | ||
| 1472 | + } | ||
| 1473 | + | ||
| 1474 | + qDebug("FNIR @ FPIR = 0.1: %.3f", 1-getOperatingPointGivenFAR(operatingPoints, 0.1).TAR); | ||
| 1475 | + qDebug("FNIR @ FPIR = 0.01: %.3f", 1-getOperatingPointGivenFAR(operatingPoints, 0.01).TAR); | ||
| 1454 | } | 1476 | } |
| 1455 | 1477 | ||
| 1456 | } // namespace br | 1478 | } // namespace br |
openbr/core/eval.h
| @@ -33,7 +33,7 @@ namespace br | @@ -33,7 +33,7 @@ namespace br | ||
| 33 | float EvalDetection(const QString &predictedGallery, const QString &truthGallery, const QString &csv = "", bool normalize = false, int minSize = 0, int maxSize = 0); // Return average overlap | 33 | float EvalDetection(const QString &predictedGallery, const QString &truthGallery, const QString &csv = "", bool normalize = false, int minSize = 0, int maxSize = 0); // Return average overlap |
| 34 | float EvalLandmarking(const QString &predictedGallery, const QString &truthGallery, const QString &csv = "", int normalizationIndexA = 0, int normalizationIndexB = 1, int sampleIndex = 0, int totalExamples = 5); // Return average error | 34 | float EvalLandmarking(const QString &predictedGallery, const QString &truthGallery, const QString &csv = "", int normalizationIndexA = 0, int normalizationIndexB = 1, int sampleIndex = 0, int totalExamples = 5); // Return average error |
| 35 | void EvalRegression(const QString &predictedGallery, const QString &truthGallery, QString predictedProperty = "", QString truthProperty = ""); | 35 | void EvalRegression(const QString &predictedGallery, const QString &truthGallery, QString predictedProperty = "", QString truthProperty = ""); |
| 36 | - void EvalKNN(const QString &knnGraph, const QString &knnTruth, const QString &iet); | 36 | + void EvalKNN(const QString &knnGraph, const QString &knnTruth, const QString &csv = ""); |
| 37 | 37 | ||
| 38 | struct Candidate | 38 | struct Candidate |
| 39 | { | 39 | { |
openbr/core/opencvutils.cpp
| @@ -436,7 +436,7 @@ public: | @@ -436,7 +436,7 @@ public: | ||
| 436 | }; | 436 | }; |
| 437 | 437 | ||
| 438 | // TODO: Make sure case where no confidences are inputted works. | 438 | // TODO: Make sure case where no confidences are inputted works. |
| 439 | -void OpenCVUtils::group(QList<Rect> &rects, QList<float> &confidences, float confidenceThreshold, float epsilon) | 439 | +void OpenCVUtils::group(QList<Rect> &rects, QList<float> &confidences, float confidenceThreshold, int minNeighbors, float epsilon) |
| 440 | { | 440 | { |
| 441 | if (rects.isEmpty()) | 441 | if (rects.isEmpty()) |
| 442 | return; | 442 | return; |
| @@ -450,7 +450,7 @@ void OpenCVUtils::group(QList<Rect> &rects, QList<float> &confidences, float con | @@ -450,7 +450,7 @@ void OpenCVUtils::group(QList<Rect> &rects, QList<float> &confidences, float con | ||
| 450 | vector<Rect> rrects(nClasses); | 450 | vector<Rect> rrects(nClasses); |
| 451 | 451 | ||
| 452 | // Total number of rects in each class | 452 | // Total number of rects in each class |
| 453 | - vector<int> rweights(nClasses, 0); | 453 | + vector<int> neighbors(nClasses, 0); |
| 454 | vector<float> rejectWeights(nClasses, -std::numeric_limits<float>::max()); | 454 | vector<float> rejectWeights(nClasses, -std::numeric_limits<float>::max()); |
| 455 | 455 | ||
| 456 | for (size_t i = 0; i < labels.size(); i++) | 456 | for (size_t i = 0; i < labels.size(); i++) |
| @@ -460,7 +460,7 @@ void OpenCVUtils::group(QList<Rect> &rects, QList<float> &confidences, float con | @@ -460,7 +460,7 @@ void OpenCVUtils::group(QList<Rect> &rects, QList<float> &confidences, float con | ||
| 460 | rrects[cls].y += rects[i].y; | 460 | rrects[cls].y += rects[i].y; |
| 461 | rrects[cls].width += rects[i].width; | 461 | rrects[cls].width += rects[i].width; |
| 462 | rrects[cls].height += rects[i].height; | 462 | rrects[cls].height += rects[i].height; |
| 463 | - rweights[cls]++; | 463 | + neighbors[cls]++; |
| 464 | } | 464 | } |
| 465 | 465 | ||
| 466 | if (useConfidences) | 466 | if (useConfidences) |
| @@ -478,7 +478,7 @@ void OpenCVUtils::group(QList<Rect> &rects, QList<float> &confidences, float con | @@ -478,7 +478,7 @@ void OpenCVUtils::group(QList<Rect> &rects, QList<float> &confidences, float con | ||
| 478 | for (int i = 0; i < nClasses; i++) | 478 | for (int i = 0; i < nClasses; i++) |
| 479 | { | 479 | { |
| 480 | Rect r = rrects[i]; | 480 | Rect r = rrects[i]; |
| 481 | - float s = 1.f/rweights[i]; | 481 | + float s = 1.f/neighbors[i]; |
| 482 | rrects[i] = Rect(saturate_cast<int>(r.x*s), | 482 | rrects[i] = Rect(saturate_cast<int>(r.x*s), |
| 483 | saturate_cast<int>(r.y*s), | 483 | saturate_cast<int>(r.y*s), |
| 484 | saturate_cast<int>(r.width*s), | 484 | saturate_cast<int>(r.width*s), |
| @@ -488,7 +488,7 @@ void OpenCVUtils::group(QList<Rect> &rects, QList<float> &confidences, float con | @@ -488,7 +488,7 @@ void OpenCVUtils::group(QList<Rect> &rects, QList<float> &confidences, float con | ||
| 488 | rects.clear(); | 488 | rects.clear(); |
| 489 | confidences.clear(); | 489 | confidences.clear(); |
| 490 | 490 | ||
| 491 | - // Aggregate by comparing average rectangles against other average rectangels | 491 | + // Aggregate by comparing average rectangles against other average rectangles |
| 492 | for (int i = 0; i < nClasses; i++) | 492 | for (int i = 0; i < nClasses; i++) |
| 493 | { | 493 | { |
| 494 | // Average rectangle | 494 | // Average rectangle |
| @@ -496,19 +496,22 @@ void OpenCVUtils::group(QList<Rect> &rects, QList<float> &confidences, float con | @@ -496,19 +496,22 @@ void OpenCVUtils::group(QList<Rect> &rects, QList<float> &confidences, float con | ||
| 496 | 496 | ||
| 497 | // Used to eliminate rectangles with few neighbors in the case of no weights | 497 | // Used to eliminate rectangles with few neighbors in the case of no weights |
| 498 | // int n1 = levelWeights ? rejectLevels[i] : rweights[i]; | 498 | // int n1 = levelWeights ? rejectLevels[i] : rweights[i]; |
| 499 | - float w1 = rejectWeights[i]; | 499 | + const float w1 = rejectWeights[i]; |
| 500 | 500 | ||
| 501 | // Eliminate rectangle if it doesn't meet confidence criteria | 501 | // Eliminate rectangle if it doesn't meet confidence criteria |
| 502 | if (w1 <= confidenceThreshold) | 502 | if (w1 <= confidenceThreshold) |
| 503 | continue; | 503 | continue; |
| 504 | 504 | ||
| 505 | + const int n1 = neighbors[i]; | ||
| 506 | + if (n1 < minNeighbors) | ||
| 507 | + continue; | ||
| 508 | + | ||
| 505 | // filter out small face rectangles inside large rectangles | 509 | // filter out small face rectangles inside large rectangles |
| 506 | int j; | 510 | int j; |
| 507 | for (j = 0; j < nClasses; j++) | 511 | for (j = 0; j < nClasses; j++) |
| 508 | { | 512 | { |
| 509 | - float w2 = rejectWeights[j]; | ||
| 510 | - | ||
| 511 | - if (j == i) | 513 | + const int n2 = neighbors[j]; |
| 514 | + if (j == i || n2 < minNeighbors) | ||
| 512 | continue; | 515 | continue; |
| 513 | 516 | ||
| 514 | Rect r2 = rrects[j]; | 517 | Rect r2 = rrects[j]; |
| @@ -516,13 +519,11 @@ void OpenCVUtils::group(QList<Rect> &rects, QList<float> &confidences, float con | @@ -516,13 +519,11 @@ void OpenCVUtils::group(QList<Rect> &rects, QList<float> &confidences, float con | ||
| 516 | int dx = saturate_cast<int>(r2.width * epsilon); | 519 | int dx = saturate_cast<int>(r2.width * epsilon); |
| 517 | int dy = saturate_cast<int>(r2.height * epsilon); | 520 | int dy = saturate_cast<int>(r2.height * epsilon); |
| 518 | 521 | ||
| 522 | + float w2 = rejectWeights[j]; | ||
| 523 | + | ||
| 519 | // If, r1 is within the r2 AND | 524 | // If, r1 is within the r2 AND |
| 520 | - // the second rectangle reaches a later stage than the first | ||
| 521 | - // where both the first and the second must have a stage greater than three OR | ||
| 522 | - // the first doens't reach the third stage. | ||
| 523 | - // Changeto: second rectangle has a higher confidence than the first OR | ||
| 524 | - // the first has a low confidence. | ||
| 525 | - // Then, eliminate the first rectangle. | 525 | + // r2 has a higher confidence than r1 |
| 526 | + // then, eliminate the r1 | ||
| 526 | if(r1.x >= r2.x - dx && | 527 | if(r1.x >= r2.x - dx && |
| 527 | r1.y >= r2.y - dy && | 528 | r1.y >= r2.y - dy && |
| 528 | r1.x + r1.width <= r2.x + r2.width + dx && | 529 | r1.x + r1.width <= r2.x + r2.width + dx && |
| @@ -531,7 +532,6 @@ void OpenCVUtils::group(QList<Rect> &rects, QList<float> &confidences, float con | @@ -531,7 +532,6 @@ void OpenCVUtils::group(QList<Rect> &rects, QList<float> &confidences, float con | ||
| 531 | break; | 532 | break; |
| 532 | } | 533 | } |
| 533 | 534 | ||
| 534 | - // Need to return rects and confidences | ||
| 535 | if( j == nClasses ) | 535 | if( j == nClasses ) |
| 536 | { | 536 | { |
| 537 | rects.append(r1); | 537 | rects.append(r1); |
openbr/core/opencvutils.h
| @@ -102,7 +102,7 @@ namespace OpenCVUtils | @@ -102,7 +102,7 @@ namespace OpenCVUtils | ||
| 102 | float overlap(const QRectF &rect1, const QRectF &rect2); | 102 | float overlap(const QRectF &rect1, const QRectF &rect2); |
| 103 | 103 | ||
| 104 | // Misc | 104 | // Misc |
| 105 | - void group(QList<cv::Rect> &rects, QList<float> &confidences, float confidenceThreshold, float epsilon); | 105 | + void group(QList<cv::Rect> &rects, QList<float> &confidences, float confidenceThreshold, int minNeighbors, float epsilon); |
| 106 | void flip(const br::Template &src, br::Template &dst, int axis, bool flipMat=true, bool flipPoints=true, bool flipRects=true); | 106 | void flip(const br::Template &src, br::Template &dst, int axis, bool flipMat=true, bool flipPoints=true, bool flipRects=true); |
| 107 | void flip(const br::TemplateList &src, br::TemplateList &dst, int axis, bool flipMat=true, bool flipPoints=true, bool flipRects=true); | 107 | void flip(const br::TemplateList &src, br::TemplateList &dst, int axis, bool flipMat=true, bool flipPoints=true, bool flipRects=true); |
| 108 | 108 |
openbr/core/plot.cpp
| @@ -346,4 +346,32 @@ bool PlotMetadata(const QStringList &files, const QString &columns, bool show) | @@ -346,4 +346,32 @@ bool PlotMetadata(const QStringList &files, const QString &columns, bool show) | ||
| 346 | return p.finalize(show); | 346 | return p.finalize(show); |
| 347 | } | 347 | } |
| 348 | 348 | ||
| 349 | +bool PlotKNN(const QStringList &files, const File &destination, bool show) | ||
| 350 | +{ | ||
| 351 | + qDebug("Plotting %d k-NN file(s) to %s", files.size(), qPrintable(destination)); | ||
| 352 | + RPlot p(files, destination); | ||
| 353 | + p.file.write("\nformatData(type=\"knn\")\n\n"); | ||
| 354 | + | ||
| 355 | + QMap<QString,File> optMap; | ||
| 356 | + optMap.insert("rocOptions", File(QString("[xTitle=False Positive Identification Rate (FPIR),yTitle=True Positive Identification Rate (TPIR),xLog=true,yLog=false]"))); | ||
| 357 | + optMap.insert("ietOptions", File(QString("[xTitle=False Positive Identification Rate (FPIR),yTitle=False Negative Identification Rate (FNIR),xLog=true,yLog=true]"))); | ||
| 358 | + optMap.insert("cmcOptions", File(QString("[xTitle=Rank,yTitle=Retrieval Rate,xLog=true,yLog=false,size=1,xLabels=(1,5,10,50,100),xBreaks=(1,5,10,50,100)]"))); | ||
| 359 | + | ||
| 360 | + foreach (const QString &key, optMap.keys()) { | ||
| 361 | + const QStringList options = destination.get<QStringList>(key, QStringList()); | ||
| 362 | + foreach (const QString &option, options) { | ||
| 363 | + QStringList words = QtUtils::parse(option, '='); | ||
| 364 | + QtUtils::checkArgsSize(words[0], words, 1, 2); | ||
| 365 | + optMap[key].set(words[0], words[1]); | ||
| 366 | + } | ||
| 367 | + } | ||
| 368 | + | ||
| 369 | + QString plot = "plot <- plotLine(lineData=%1, options=list(%2), flipY=%3)\nplot\n"; | ||
| 370 | + p.file.write(qPrintable(QString(plot).arg("IET", toRList(optMap["rocOptions"]), "TRUE"))); | ||
| 371 | + p.file.write(qPrintable(QString(plot).arg("IET", toRList(optMap["ietOptions"]), "FALSE"))); | ||
| 372 | + p.file.write(qPrintable(QString(plot).arg("CMC", toRList(optMap["cmcOptions"]), "FALSE"))); | ||
| 373 | + | ||
| 374 | + return p.finalize(show); | ||
| 375 | +} | ||
| 376 | + | ||
| 349 | } // namespace br | 377 | } // namespace br |
openbr/core/plot.h
| @@ -28,6 +28,7 @@ namespace br | @@ -28,6 +28,7 @@ namespace br | ||
| 28 | bool PlotDetection(const QStringList &files, const File &destination, bool show = false); | 28 | bool PlotDetection(const QStringList &files, const File &destination, bool show = false); |
| 29 | bool PlotLandmarking(const QStringList &files, const File &destination, bool show = false); | 29 | bool PlotLandmarking(const QStringList &files, const File &destination, bool show = false); |
| 30 | bool PlotMetadata(const QStringList &files, const QString &destination, bool show = false); | 30 | bool PlotMetadata(const QStringList &files, const QString &destination, bool show = false); |
| 31 | + bool PlotKNN(const QStringList &files, const File &destination, bool show = false); | ||
| 31 | } | 32 | } |
| 32 | 33 | ||
| 33 | #endif // BR_PLOT_H | 34 | #endif // BR_PLOT_H |
openbr/openbr.cpp
| @@ -145,9 +145,9 @@ void br_eval_regression(const char *predicted_gallery, const char *truth_gallery | @@ -145,9 +145,9 @@ void br_eval_regression(const char *predicted_gallery, const char *truth_gallery | ||
| 145 | EvalRegression(predicted_gallery, truth_gallery, predicted_property, truth_property); | 145 | EvalRegression(predicted_gallery, truth_gallery, predicted_property, truth_property); |
| 146 | } | 146 | } |
| 147 | 147 | ||
| 148 | -void br_eval_knn(const char *knnGraph, const char *knnTruth, const char *iet) | 148 | +void br_eval_knn(const char *knnGraph, const char *knnTruth, const char *csv) |
| 149 | { | 149 | { |
| 150 | - EvalKNN(knnGraph, knnTruth, iet); | 150 | + EvalKNN(knnGraph, knnTruth, csv); |
| 151 | } | 151 | } |
| 152 | 152 | ||
| 153 | void br_finalize() | 153 | void br_finalize() |
| @@ -221,6 +221,11 @@ bool br_plot_metadata(int num_files, const char *files[], const char *columns, b | @@ -221,6 +221,11 @@ bool br_plot_metadata(int num_files, const char *files[], const char *columns, b | ||
| 221 | return PlotMetadata(QtUtils::toStringList(num_files, files), columns, show); | 221 | return PlotMetadata(QtUtils::toStringList(num_files, files), columns, show); |
| 222 | } | 222 | } |
| 223 | 223 | ||
| 224 | +bool br_plot_knn(int num_files, const char *files[], const char *destination, bool show) | ||
| 225 | +{ | ||
| 226 | + return PlotKNN(QtUtils::toStringList(num_files, files), destination, show); | ||
| 227 | +} | ||
| 228 | + | ||
| 224 | float br_progress() | 229 | float br_progress() |
| 225 | { | 230 | { |
| 226 | return Globals->progress(); | 231 | return Globals->progress(); |
openbr/openbr.h
| @@ -64,7 +64,7 @@ BR_EXPORT float br_eval_landmarking(const char *predicted_gallery, const char *t | @@ -64,7 +64,7 @@ BR_EXPORT float br_eval_landmarking(const char *predicted_gallery, const char *t | ||
| 64 | 64 | ||
| 65 | BR_EXPORT void br_eval_regression(const char *predicted_gallery, const char *truth_gallery, const char *predicted_property = "", const char *truth_property = ""); | 65 | BR_EXPORT void br_eval_regression(const char *predicted_gallery, const char *truth_gallery, const char *predicted_property = "", const char *truth_property = ""); |
| 66 | 66 | ||
| 67 | -BR_EXPORT void br_eval_knn(const char *knnGraph, const char *knnTruth, const char *iet); | 67 | +BR_EXPORT void br_eval_knn(const char *knnGraph, const char *knnTruth, const char *csv = ""); |
| 68 | 68 | ||
| 69 | BR_EXPORT void br_finalize(); | 69 | BR_EXPORT void br_finalize(); |
| 70 | 70 | ||
| @@ -93,6 +93,8 @@ BR_EXPORT bool br_plot_landmarking(int num_files, const char *files[], const cha | @@ -93,6 +93,8 @@ BR_EXPORT bool br_plot_landmarking(int num_files, const char *files[], const cha | ||
| 93 | 93 | ||
| 94 | BR_EXPORT bool br_plot_metadata(int num_files, const char *files[], const char *columns, bool show = false); | 94 | BR_EXPORT bool br_plot_metadata(int num_files, const char *files[], const char *columns, bool show = false); |
| 95 | 95 | ||
| 96 | +BR_EXPORT bool br_plot_knn(int num_files, const char *files[], const char *destination, bool show = false); | ||
| 97 | + | ||
| 96 | BR_EXPORT float br_progress(); | 98 | BR_EXPORT float br_progress(); |
| 97 | 99 | ||
| 98 | BR_EXPORT void br_read_pipe(const char *pipe, int *argc, char ***argv); | 100 | BR_EXPORT void br_read_pipe(const char *pipe, int *argc, char ***argv); |
openbr/openbr_plugin.h
| @@ -882,13 +882,11 @@ public: | @@ -882,13 +882,11 @@ public: | ||
| 882 | 882 | ||
| 883 | static Representation *make(QString str, QObject *parent); /*!< \brief Make a representation from a string. */ | 883 | static Representation *make(QString str, QObject *parent); /*!< \brief Make a representation from a string. */ |
| 884 | 884 | ||
| 885 | - virtual void preprocess(const cv::Mat &src, cv::Mat &dst) const { dst = src; } | ||
| 886 | - virtual void train(const QList<cv::Mat> &images, const QList<float> &labels) { (void) images; (void)labels; } | ||
| 887 | - | ||
| 888 | - virtual float evaluate(const cv::Mat &image, int idx) const = 0; | ||
| 889 | - // By convention, an empty indices list will result in all feature responses being calculated | ||
| 890 | - // and returned. | ||
| 891 | - virtual cv::Mat evaluate(const cv::Mat &image, const QList<int> &indices = QList<int>()) const = 0; | 885 | + virtual Template preprocess(const Template &src) const { return src; } |
| 886 | + virtual void train(const TemplateList &data) { (void)data; } | ||
| 887 | + virtual float evaluate(const Template &src, int idx) const = 0; | ||
| 888 | + // By convention passing an empty list evaluates all features in the representation | ||
| 889 | + virtual cv::Mat evaluate(const Template &src, const QList<int> &indices = QList<int>()) const = 0; | ||
| 892 | 890 | ||
| 893 | virtual cv::Size windowSize(int *dx = NULL, int *dy = NULL) const = 0; // dx and dy should indicate the change to the original window size after preprocessing | 891 | virtual cv::Size windowSize(int *dx = NULL, int *dy = NULL) const = 0; // dx and dy should indicate the change to the original window size after preprocessing |
| 894 | virtual int numChannels() const { return 1; } | 892 | virtual int numChannels() const { return 1; } |
| @@ -905,13 +903,13 @@ public: | @@ -905,13 +903,13 @@ public: | ||
| 905 | 903 | ||
| 906 | static Classifier *make(QString str, QObject *parent); | 904 | static Classifier *make(QString str, QObject *parent); |
| 907 | 905 | ||
| 908 | - virtual void train(const QList<cv::Mat> &images, const QList<float> &labels) = 0; | ||
| 909 | - virtual float classify(const cv::Mat &image, bool process = true, float *confidence = NULL) const = 0; | 906 | + virtual void train(const TemplateList &data) { (void)data; } |
| 907 | + virtual float classify(const Template &src, bool process = true, float *confidence = NULL) const = 0; | ||
| 910 | 908 | ||
| 911 | // Slots for representations | 909 | // Slots for representations |
| 912 | - virtual cv::Mat preprocess(const cv::Mat &image) const = 0; | 910 | + virtual Template preprocess(const Template &src) const { return src; } |
| 913 | virtual cv::Size windowSize(int *dx = NULL, int *dy = NULL) const = 0; | 911 | virtual cv::Size windowSize(int *dx = NULL, int *dy = NULL) const = 0; |
| 914 | - virtual int numFeatures() const = 0; | 912 | + virtual int numFeatures() const { return 0; } |
| 915 | }; | 913 | }; |
| 916 | 914 | ||
| 917 | 915 |
openbr/plugins/classification/boostedforest.cpp
| @@ -125,20 +125,20 @@ private: | @@ -125,20 +125,20 @@ private: | ||
| 125 | QList<Node*> classifiers; | 125 | QList<Node*> classifiers; |
| 126 | float threshold; | 126 | float threshold; |
| 127 | 127 | ||
| 128 | - void train(const QList<Mat> &images, const QList<float> &labels) | 128 | + void train(const TemplateList &data) |
| 129 | { | 129 | { |
| 130 | - representation->train(images, labels); | 130 | + representation->train(data); |
| 131 | 131 | ||
| 132 | CascadeBoostParams params(type, minTAR, maxFAR, trimRate, maxDepth, maxWeakCount); | 132 | CascadeBoostParams params(type, minTAR, maxFAR, trimRate, maxDepth, maxWeakCount); |
| 133 | 133 | ||
| 134 | FeatureEvaluator featureEvaluator; | 134 | FeatureEvaluator featureEvaluator; |
| 135 | - featureEvaluator.init(representation, images.size(), representation->numChannels()); | 135 | + featureEvaluator.init(representation, data.size()); |
| 136 | 136 | ||
| 137 | - for (int i = 0; i < images.size(); i++) | ||
| 138 | - featureEvaluator.setImage(images[i], labels[i], i); | 137 | + for (int i = 0; i < data.size(); i++) |
| 138 | + featureEvaluator.setImage(data[i], data[i].file.get<float>("Label"), i); | ||
| 139 | 139 | ||
| 140 | CascadeBoost boost; | 140 | CascadeBoost boost; |
| 141 | - boost.train(&featureEvaluator, images.size(), 1024, 1024, representation->numChannels(), params); | 141 | + boost.train(&featureEvaluator, data.size(), 1024, 1024, representation->numChannels(), params); |
| 142 | 142 | ||
| 143 | threshold = boost.getThreshold(); | 143 | threshold = boost.getThreshold(); |
| 144 | 144 | ||
| @@ -149,13 +149,9 @@ private: | @@ -149,13 +149,9 @@ private: | ||
| 149 | } | 149 | } |
| 150 | } | 150 | } |
| 151 | 151 | ||
| 152 | - float classify(const Mat &image, bool process, float *confidence) const | 152 | + float classify(const Template &src, bool process, float *confidence) const |
| 153 | { | 153 | { |
| 154 | - Mat m; | ||
| 155 | - if (process) | ||
| 156 | - m = preprocess(image); | ||
| 157 | - else | ||
| 158 | - m = image; | 154 | + Template t = process ? preprocess(src) : src; |
| 159 | 155 | ||
| 160 | float sum = 0; | 156 | float sum = 0; |
| 161 | for (int i = 0; i < classifiers.size(); i++) { | 157 | for (int i = 0; i < classifiers.size(); i++) { |
| @@ -163,10 +159,10 @@ private: | @@ -163,10 +159,10 @@ private: | ||
| 163 | 159 | ||
| 164 | while (node->left) { | 160 | while (node->left) { |
| 165 | if (representation->maxCatCount() > 0) { | 161 | if (representation->maxCatCount() > 0) { |
| 166 | - int c = (int)representation->evaluate(m, node->featureIdx); | 162 | + int c = (int)representation->evaluate(t, node->featureIdx); |
| 167 | node = (node->subset[c >> 5] & (1 << (c & 31))) ? node->left : node->right; | 163 | node = (node->subset[c >> 5] & (1 << (c & 31))) ? node->left : node->right; |
| 168 | } else { | 164 | } else { |
| 169 | - double val = representation->evaluate(m, node->featureIdx); | 165 | + double val = representation->evaluate(t, node->featureIdx); |
| 170 | node = val <= node->threshold ? node->left : node->right; | 166 | node = val <= node->threshold ? node->left : node->right; |
| 171 | } | 167 | } |
| 172 | } | 168 | } |
| @@ -184,11 +180,9 @@ private: | @@ -184,11 +180,9 @@ private: | ||
| 184 | return representation->numFeatures(); | 180 | return representation->numFeatures(); |
| 185 | } | 181 | } |
| 186 | 182 | ||
| 187 | - Mat preprocess(const Mat &image) const | 183 | + Template preprocess(const Template &src) const |
| 188 | { | 184 | { |
| 189 | - Mat dst; | ||
| 190 | - representation->preprocess(image, dst); | ||
| 191 | - return dst; | 185 | + return representation->preprocess(src); |
| 192 | } | 186 | } |
| 193 | 187 | ||
| 194 | Size windowSize(int *dx, int *dy) const | 188 | Size windowSize(int *dx, int *dy) const |
openbr/plugins/classification/cascade.cpp
| 1 | #include <opencv2/imgproc/imgproc.hpp> | 1 | #include <opencv2/imgproc/imgproc.hpp> |
| 2 | -#include <opencv2/highgui/highgui.hpp> | 2 | + |
| 3 | #include <openbr/plugins/openbr_internal.h> | 3 | #include <openbr/plugins/openbr_internal.h> |
| 4 | #include <openbr/core/common.h> | 4 | #include <openbr/core/common.h> |
| 5 | +#include "openbr/core/opencvutils.h" | ||
| 5 | 6 | ||
| 6 | #include <QtConcurrent> | 7 | #include <QtConcurrent> |
| 7 | 8 | ||
| @@ -37,7 +38,7 @@ struct Miner | @@ -37,7 +38,7 @@ struct Miner | ||
| 37 | Mat mine(bool *newImg) | 38 | Mat mine(bool *newImg) |
| 38 | { | 39 | { |
| 39 | // Copy region of winSize region of img into m | 40 | // Copy region of winSize region of img into m |
| 40 | - Mat window(windowSize.height, windowSize.width, CV_8UC1, | 41 | + Mat window(windowSize.height, windowSize.width, CV_8U, |
| 41 | (void*)(scaledSrc.data + point.y * scaledSrc.step + point.x * scaledSrc.elemSize()), | 42 | (void*)(scaledSrc.data + point.y * scaledSrc.step + point.x * scaledSrc.elemSize()), |
| 42 | scaledSrc.step); | 43 | scaledSrc.step); |
| 43 | 44 | ||
| @@ -100,31 +101,30 @@ class CascadeClassifier : public Classifier | @@ -100,31 +101,30 @@ class CascadeClassifier : public Classifier | ||
| 100 | BR_PROPERTY(bool, requireAllStages, false) | 101 | BR_PROPERTY(bool, requireAllStages, false) |
| 101 | 102 | ||
| 102 | QList<Classifier *> stages; | 103 | QList<Classifier *> stages; |
| 103 | - QList<Mat> posImages, negImages; | ||
| 104 | - QList<Mat> posSamples, negSamples; | 104 | + TemplateList posImages, negImages; |
| 105 | + TemplateList posSamples, negSamples; | ||
| 105 | 106 | ||
| 106 | QList<int> indices; | 107 | QList<int> indices; |
| 107 | int negIndex, posIndex, samplingRound; | 108 | int negIndex, posIndex, samplingRound; |
| 108 | 109 | ||
| 109 | - QMutex samplingMutex, miningMutex, passedMutex; | 110 | + QMutex samplingMutex, miningMutex; |
| 110 | 111 | ||
| 111 | void init() | 112 | void init() |
| 112 | { | 113 | { |
| 113 | negIndex = posIndex = samplingRound = 0; | 114 | negIndex = posIndex = samplingRound = 0; |
| 114 | } | 115 | } |
| 115 | 116 | ||
| 116 | - bool getPositive(Mat &img) | 117 | + bool getPositive(Template &img) |
| 117 | { | 118 | { |
| 118 | if (posIndex >= posImages.size()) | 119 | if (posIndex >= posImages.size()) |
| 119 | return false; | 120 | return false; |
| 120 | - | ||
| 121 | - posImages[indices[posIndex++]].copyTo(img); | 121 | + img = posImages[indices[posIndex++]]; |
| 122 | return true; | 122 | return true; |
| 123 | } | 123 | } |
| 124 | 124 | ||
| 125 | - Mat getNegative(Point &offset) | 125 | + Template getNegative(Point &offset) |
| 126 | { | 126 | { |
| 127 | - Mat negative; | 127 | + Template negative; |
| 128 | 128 | ||
| 129 | const Size size = windowSize(); | 129 | const Size size = windowSize(); |
| 130 | // Grab negative from list | 130 | // Grab negative from list |
| @@ -136,9 +136,9 @@ class CascadeClassifier : public Classifier | @@ -136,9 +136,9 @@ class CascadeClassifier : public Classifier | ||
| 136 | samplingRound = samplingRound % (size.width * size.height); | 136 | samplingRound = samplingRound % (size.width * size.height); |
| 137 | negIndex %= count; | 137 | negIndex %= count; |
| 138 | 138 | ||
| 139 | - offset.x = qMin( (int)samplingRound % size.width, negative.cols - size.width); | ||
| 140 | - offset.y = qMin( (int)samplingRound / size.width, negative.rows - size.height); | ||
| 141 | - if (!negative.empty() && negative.type() == CV_8UC1 | 139 | + offset.x = qMin( (int)samplingRound % size.width, negative.m().cols - size.width); |
| 140 | + offset.y = qMin( (int)samplingRound / size.width, negative.m().rows - size.height); | ||
| 141 | + if (!negative.m().empty() && negative.m().type() == CV_8U | ||
| 142 | && offset.x >= 0 && offset.y >= 0) | 142 | && offset.x >= 0 && offset.y >= 0) |
| 143 | break; | 143 | break; |
| 144 | } | 144 | } |
| @@ -146,35 +146,32 @@ class CascadeClassifier : public Classifier | @@ -146,35 +146,32 @@ class CascadeClassifier : public Classifier | ||
| 146 | return negative; | 146 | return negative; |
| 147 | } | 147 | } |
| 148 | 148 | ||
| 149 | - int mine() | 149 | + uint64 mine() |
| 150 | { | 150 | { |
| 151 | - int passedNegatives = 0; | 151 | + uint64 passedNegatives = 0; |
| 152 | forever { | 152 | forever { |
| 153 | - Mat negative; | 153 | + Template negative; |
| 154 | Point offset; | 154 | Point offset; |
| 155 | - samplingMutex.lock(); | 155 | + QMutexLocker samplingLocker(&samplingMutex); |
| 156 | negative = getNegative(offset); | 156 | negative = getNegative(offset); |
| 157 | - samplingMutex.unlock(); | 157 | + samplingLocker.unlock(); |
| 158 | 158 | ||
| 159 | - Miner miner(negative, windowSize(), offset); | 159 | + Miner miner(negative.m(), windowSize(), offset); |
| 160 | forever { | 160 | forever { |
| 161 | bool newImg; | 161 | bool newImg; |
| 162 | - Mat sample = miner.mine(&newImg); | 162 | + Template sample(negative.file, miner.mine(&newImg)); |
| 163 | if (!newImg) { | 163 | if (!newImg) { |
| 164 | if (negSamples.size() >= numNegs) | 164 | if (negSamples.size() >= numNegs) |
| 165 | return passedNegatives; | 165 | return passedNegatives; |
| 166 | 166 | ||
| 167 | float confidence; | 167 | float confidence; |
| 168 | if (classify(sample, true, &confidence) != 0) { | 168 | if (classify(sample, true, &confidence) != 0) { |
| 169 | - miningMutex.lock(); | ||
| 170 | - if (negSamples.size() >= numNegs) { | ||
| 171 | - miningMutex.unlock(); | 169 | + QMutexLocker miningLocker(&miningMutex); |
| 170 | + if (negSamples.size() >= numNegs) | ||
| 172 | return passedNegatives; | 171 | return passedNegatives; |
| 173 | - } | ||
| 174 | 172 | ||
| 175 | negSamples.append(sample); | 173 | negSamples.append(sample); |
| 176 | printf("Negative samples: %d\r", negSamples.size()); | 174 | printf("Negative samples: %d\r", negSamples.size()); |
| 177 | - miningMutex.unlock(); | ||
| 178 | } | 175 | } |
| 179 | 176 | ||
| 180 | passedNegatives++; | 177 | passedNegatives++; |
| @@ -184,16 +181,16 @@ class CascadeClassifier : public Classifier | @@ -184,16 +181,16 @@ class CascadeClassifier : public Classifier | ||
| 184 | } | 181 | } |
| 185 | } | 182 | } |
| 186 | 183 | ||
| 187 | - void train(const QList<Mat> &images, const QList<float> &labels) | 184 | + void train(const TemplateList &data) |
| 188 | { | 185 | { |
| 189 | - for (int i = 0; i < images.size(); i++) | ||
| 190 | - labels[i] == 1 ? posImages.append(images[i]) : negImages.append(images[i]); | 186 | + foreach (const Template &t, data) |
| 187 | + t.file.get<float>("Label") == 1.0f ? posImages.append(t) : negImages.append(t); | ||
| 191 | 188 | ||
| 192 | - qDebug() << "Total images:" << images.size() | 189 | + qDebug() << "Total images:" << data.size() |
| 193 | << "\nTotal positive images:" << posImages.size() | 190 | << "\nTotal positive images:" << posImages.size() |
| 194 | << "\nTotal negative images:" << negImages.size(); | 191 | << "\nTotal negative images:" << negImages.size(); |
| 195 | 192 | ||
| 196 | - indices = Common::RandSample(posImages.size(),posImages.size(),true); | 193 | + indices = Common::RandSample(posImages.size(), posImages.size(), true); |
| 197 | 194 | ||
| 198 | stages.reserve(numStages); | 195 | stages.reserve(numStages); |
| 199 | for (int i = 0; i < numStages; i++) { | 196 | for (int i = 0; i < numStages; i++) { |
| @@ -212,27 +209,17 @@ class CascadeClassifier : public Classifier | @@ -212,27 +209,17 @@ class CascadeClassifier : public Classifier | ||
| 212 | return; | 209 | return; |
| 213 | } | 210 | } |
| 214 | 211 | ||
| 215 | - QList<float> posLabels; | ||
| 216 | - posLabels.reserve(posSamples.size()); | ||
| 217 | - for (int j=0; j<posSamples.size(); j++) | ||
| 218 | - posLabels.append(1); | ||
| 219 | - | ||
| 220 | - QList<float> negLabels; | ||
| 221 | - negLabels.reserve(negSamples.size()); | ||
| 222 | - for (int j=0; j<negSamples.size(); j++) | ||
| 223 | - negLabels.append(0); | ||
| 224 | - | ||
| 225 | - stages[i]->train(posSamples+negSamples, posLabels+negLabels); | 212 | + stages[i]->train(posSamples + negSamples); |
| 226 | 213 | ||
| 227 | qDebug() << "END>"; | 214 | qDebug() << "END>"; |
| 228 | } | 215 | } |
| 229 | } | 216 | } |
| 230 | 217 | ||
| 231 | - float classify(const Mat &image, bool process, float *confidence) const | 218 | + float classify(const Template &src, bool process, float *confidence) const |
| 232 | { | 219 | { |
| 233 | float stageConf = 0.0f; | 220 | float stageConf = 0.0f; |
| 234 | foreach (const Classifier *stage, stages) { | 221 | foreach (const Classifier *stage, stages) { |
| 235 | - float result = stage->classify(image, process, &stageConf); | 222 | + float result = stage->classify(src, process, &stageConf); |
| 236 | if (confidence) | 223 | if (confidence) |
| 237 | *confidence += stageConf; | 224 | *confidence += stageConf; |
| 238 | if (result == 0.0f) | 225 | if (result == 0.0f) |
| @@ -246,9 +233,9 @@ class CascadeClassifier : public Classifier | @@ -246,9 +233,9 @@ class CascadeClassifier : public Classifier | ||
| 246 | return stages.first()->numFeatures(); | 233 | return stages.first()->numFeatures(); |
| 247 | } | 234 | } |
| 248 | 235 | ||
| 249 | - Mat preprocess(const Mat &image) const | 236 | + Template preprocess(const Template &src) const |
| 250 | { | 237 | { |
| 251 | - return stages.first()->preprocess(image); | 238 | + return stages.first()->preprocess(src); |
| 252 | } | 239 | } |
| 253 | 240 | ||
| 254 | Size windowSize(int *dx = NULL, int *dy = NULL) const | 241 | Size windowSize(int *dx = NULL, int *dy = NULL) const |
| @@ -276,16 +263,13 @@ class CascadeClassifier : public Classifier | @@ -276,16 +263,13 @@ class CascadeClassifier : public Classifier | ||
| 276 | private: | 263 | private: |
| 277 | float getSamples() | 264 | float getSamples() |
| 278 | { | 265 | { |
| 279 | - posSamples.clear(); | ||
| 280 | - posSamples.reserve(numPos); | ||
| 281 | - negSamples.clear(); | ||
| 282 | - negSamples.reserve(numNegs); | 266 | + posSamples.clear(); posSamples.reserve(numPos); |
| 267 | + negSamples.clear(); negSamples.reserve(numNegs); | ||
| 283 | posIndex = 0; | 268 | posIndex = 0; |
| 284 | 269 | ||
| 285 | float confidence; | 270 | float confidence; |
| 286 | while (posSamples.size() < numPos) { | 271 | while (posSamples.size() < numPos) { |
| 287 | - Mat pos(windowSize(), CV_8UC1); | ||
| 288 | - | 272 | + Template pos; |
| 289 | if (!getPositive(pos)) | 273 | if (!getPositive(pos)) |
| 290 | qFatal("Cannot get another positive sample!"); | 274 | qFatal("Cannot get another positive sample!"); |
| 291 | 275 | ||
| @@ -297,13 +281,13 @@ private: | @@ -297,13 +281,13 @@ private: | ||
| 297 | 281 | ||
| 298 | qDebug() << "POS count : consumed " << posSamples.size() << ":" << posIndex; | 282 | qDebug() << "POS count : consumed " << posSamples.size() << ":" << posIndex; |
| 299 | 283 | ||
| 300 | - QFutureSynchronizer<int> futures; | 284 | + QFutureSynchronizer<uint64> futures; |
| 301 | for (int i=0; i<QThread::idealThreadCount(); i++) | 285 | for (int i=0; i<QThread::idealThreadCount(); i++) |
| 302 | futures.addFuture(QtConcurrent::run(this, &CascadeClassifier::mine)); | 286 | futures.addFuture(QtConcurrent::run(this, &CascadeClassifier::mine)); |
| 303 | futures.waitForFinished(); | 287 | futures.waitForFinished(); |
| 304 | 288 | ||
| 305 | - int passedNegs = 0; | ||
| 306 | - QList<QFuture<int> > results = futures.futures(); | 289 | + uint64 passedNegs = 0; |
| 290 | + QList<QFuture<uint64> > results = futures.futures(); | ||
| 307 | for (int i=0; i<results.size(); i++) | 291 | for (int i=0; i<results.size(); i++) |
| 308 | passedNegs += results[i].result(); | 292 | passedNegs += results[i].result(); |
| 309 | 293 |
openbr/plugins/classification/dlib.cpp
| 1 | +#include <opencv2/imgproc/imgproc.hpp> | ||
| 1 | #include <dlib/image_processing/frontal_face_detector.h> | 2 | #include <dlib/image_processing/frontal_face_detector.h> |
| 2 | #include <dlib/opencv.h> | 3 | #include <dlib/opencv.h> |
| 3 | 4 | ||
| @@ -45,14 +46,18 @@ private: | @@ -45,14 +46,18 @@ private: | ||
| 45 | { | 46 | { |
| 46 | dst = src; | 47 | dst = src; |
| 47 | 48 | ||
| 48 | - shape_predictor *sp = shapeResource.acquire(); | 49 | + shape_predictor *const sp = shapeResource.acquire(); |
| 49 | 50 | ||
| 50 | - cv_image<unsigned char> cimg(src.m()); | 51 | + cv::Mat cvImage = src.m(); |
| 52 | + if (cvImage.channels() == 3) | ||
| 53 | + cv::cvtColor(cvImage, cvImage, CV_BGR2GRAY); | ||
| 54 | + | ||
| 55 | + cv_image<unsigned char> cimg(cvImage); | ||
| 51 | array2d<unsigned char> image; | 56 | array2d<unsigned char> image; |
| 52 | assign_image(image,cimg); | 57 | assign_image(image,cimg); |
| 53 | 58 | ||
| 54 | if (src.file.rects().isEmpty()) { //If the image has no rects assume the whole image is a face | 59 | if (src.file.rects().isEmpty()) { //If the image has no rects assume the whole image is a face |
| 55 | - rectangle r(0, 0, src.m().cols, src.m().rows); | 60 | + rectangle r(0, 0, cvImage.cols, cvImage.rows); |
| 56 | full_object_detection shape = (*sp)(image, r); | 61 | full_object_detection shape = (*sp)(image, r); |
| 57 | for (size_t i = 0; i < shape.num_parts(); i++) | 62 | for (size_t i = 0; i < shape.num_parts(); i++) |
| 58 | dst.file.appendPoint(QPointF(shape.part(i)(0),shape.part(i)(1))); | 63 | dst.file.appendPoint(QPointF(shape.part(i)(0),shape.part(i)(1))); |
openbr/plugins/classification/liblinear.cpp
| @@ -138,7 +138,7 @@ private: | @@ -138,7 +138,7 @@ private: | ||
| 138 | param.weight = NULL; | 138 | param.weight = NULL; |
| 139 | } | 139 | } |
| 140 | 140 | ||
| 141 | - m = *train_svm(&prob, ¶m); | 141 | + //m = *train_svm(&prob, ¶m); |
| 142 | 142 | ||
| 143 | delete[] param.weight; | 143 | delete[] param.weight; |
| 144 | delete[] param.weight_label; | 144 | delete[] param.weight_label; |
openbr/plugins/core/algorithms.cpp
| @@ -39,7 +39,7 @@ class AlgorithmsInitializer : public Initializer | @@ -39,7 +39,7 @@ class AlgorithmsInitializer : public Initializer | ||
| 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))"); | 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 | Globals->abbreviations.insert("FR_Nose", "(CropFromLandmarks([16,17,18,19,20,21,22,23,24,25,26,27],padding=3)+Resize(36,36))"); | 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 | Globals->abbreviations.insert("FR_Face", "(Crop(24,24,88,88)+Resize(44,44))"); | 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 | Globals->abbreviations.insert("FR_Represent", "((DenseHOG/DenseLBP)+Cat+LDA(.98)+Normalize(L2))"); | 43 | Globals->abbreviations.insert("FR_Represent", "((DenseHOG/DenseLBP)+Cat+LDA(.98)+Normalize(L2))"); |
| 44 | 44 | ||
| 45 | Globals->abbreviations.insert("GenderClassification", "FaceDetection+Expand+FaceClassificationRegistration+Expand+<FaceClassificationExtraction>+<GenderClassifier>+Discard"); | 45 | Globals->abbreviations.insert("GenderClassification", "FaceDetection+Expand+FaceClassificationRegistration+Expand+<FaceClassificationExtraction>+<GenderClassifier>+Discard"); |
openbr/plugins/distance/unit.cpp
| @@ -76,12 +76,18 @@ class UnitDistance : public Distance | @@ -76,12 +76,18 @@ class UnitDistance : public Distance | ||
| 76 | 76 | ||
| 77 | float compare(const Template &target, const Template &query) const | 77 | float compare(const Template &target, const Template &query) const |
| 78 | { | 78 | { |
| 79 | - return a * (distance->compare(target, query) - b); | 79 | + return normalize(distance->compare(target, query)); |
| 80 | } | 80 | } |
| 81 | 81 | ||
| 82 | float compare(const cv::Mat &target, const cv::Mat &query) const | 82 | float compare(const cv::Mat &target, const cv::Mat &query) const |
| 83 | { | 83 | { |
| 84 | - return a * (distance->compare(target, query) - b); | 84 | + return normalize(distance->compare(target, query)); |
| 85 | + } | ||
| 86 | + | ||
| 87 | + float normalize(float score) const | ||
| 88 | + { | ||
| 89 | + if (!Globals->scoreNormalization) return score; | ||
| 90 | + return a * (score - b); | ||
| 85 | } | 91 | } |
| 86 | }; | 92 | }; |
| 87 | 93 |
openbr/plugins/gallery/keyframes.cpp
| @@ -26,6 +26,12 @@ extern "C" | @@ -26,6 +26,12 @@ extern "C" | ||
| 26 | #include <libswscale/swscale.h> | 26 | #include <libswscale/swscale.h> |
| 27 | } | 27 | } |
| 28 | 28 | ||
| 29 | +// http://stackoverflow.com/questions/24057248/ffmpeg-undefined-references-to-av-frame-alloc | ||
| 30 | +#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55,28,1) | ||
| 31 | +#define av_frame_alloc avcodec_alloc_frame | ||
| 32 | +#define av_frame_free avcodec_free_frame | ||
| 33 | +#endif | ||
| 34 | + | ||
| 29 | using namespace cv; | 35 | using namespace cv; |
| 30 | 36 | ||
| 31 | namespace br | 37 | namespace br |
openbr/plugins/imgproc/equalizehist.cpp
| @@ -18,6 +18,8 @@ | @@ -18,6 +18,8 @@ | ||
| 18 | 18 | ||
| 19 | #include <openbr/plugins/openbr_internal.h> | 19 | #include <openbr/plugins/openbr_internal.h> |
| 20 | 20 | ||
| 21 | +using namespace cv; | ||
| 22 | + | ||
| 21 | namespace br | 23 | namespace br |
| 22 | { | 24 | { |
| 23 | 25 | ||
| @@ -32,7 +34,20 @@ class EqualizeHistTransform : public UntrainableTransform | @@ -32,7 +34,20 @@ class EqualizeHistTransform : public UntrainableTransform | ||
| 32 | 34 | ||
| 33 | void project(const Template &src, Template &dst) const | 35 | void project(const Template &src, Template &dst) const |
| 34 | { | 36 | { |
| 35 | - cv::equalizeHist(src, dst); | 37 | + if (src.m().channels() == 1) { |
| 38 | + equalizeHist(src, dst); | ||
| 39 | + } else if (src.m().channels() == 3) { | ||
| 40 | + // http://stackoverflow.com/questions/15007304/histogram-equalization-not-working-on-color-image-opencv | ||
| 41 | + Mat ycrcb; | ||
| 42 | + cvtColor(src, ycrcb, CV_BGR2YCrCb); | ||
| 43 | + vector<Mat> channels; | ||
| 44 | + split(ycrcb, channels); | ||
| 45 | + equalizeHist(channels[0], channels[0]); | ||
| 46 | + merge(channels, ycrcb); | ||
| 47 | + cvtColor(ycrcb, dst, CV_YCrCb2BGR); | ||
| 48 | + } else { | ||
| 49 | + qFatal("Invalid channel count!"); | ||
| 50 | + } | ||
| 36 | } | 51 | } |
| 37 | }; | 52 | }; |
| 38 | 53 |
openbr/plugins/imgproc/pad.cpp
| 1 | +#include <opencv2/imgproc/imgproc.hpp> | ||
| 1 | #include <openbr/plugins/openbr_internal.h> | 2 | #include <openbr/plugins/openbr_internal.h> |
| 2 | 3 | ||
| 3 | using namespace cv; | 4 | using namespace cv; |
| @@ -5,24 +6,36 @@ using namespace cv; | @@ -5,24 +6,36 @@ using namespace cv; | ||
| 5 | namespace br | 6 | namespace br |
| 6 | { | 7 | { |
| 7 | 8 | ||
| 9 | +/*! | ||
| 10 | + * \ingroup transforms | ||
| 11 | + * \brief Pads an image. | ||
| 12 | + * \author Scott Klum \cite sklum | ||
| 13 | + */ | ||
| 8 | class PadTransform : public UntrainableTransform | 14 | class PadTransform : public UntrainableTransform |
| 9 | { | 15 | { |
| 10 | Q_OBJECT | 16 | Q_OBJECT |
| 11 | - | ||
| 12 | - Q_PROPERTY(int padSize READ get_padSize WRITE set_padSize RESET reset_padSize STORED false) | ||
| 13 | - Q_PROPERTY(int padValue READ get_padValue WRITE set_padValue RESET reset_padValue STORED false) | ||
| 14 | - BR_PROPERTY(int, padSize, 0) | ||
| 15 | - BR_PROPERTY(int, padValue, 0) | 17 | + Q_ENUMS(Method) |
| 18 | + | ||
| 19 | +public: | ||
| 20 | + /*!< */ | ||
| 21 | + enum Border { Replicate = BORDER_REPLICATE, | ||
| 22 | + Reflect = BORDER_REFLECT_101, | ||
| 23 | + Constant = BORDER_CONSTANT}; | ||
| 24 | + | ||
| 25 | +private: | ||
| 26 | + Q_PROPERTY(Border border READ get_border WRITE set_border RESET reset_border STORED false) | ||
| 27 | + Q_PROPERTY(float percent READ get_percent WRITE set_percent RESET reset_percent STORED false) | ||
| 28 | + Q_PROPERTY(int value READ get_value WRITE set_value RESET reset_value STORED false) | ||
| 29 | + BR_PROPERTY(Border, border, Replicate) | ||
| 30 | + BR_PROPERTY(float, percent, .1) | ||
| 31 | + BR_PROPERTY(float, value, 0) | ||
| 16 | 32 | ||
| 17 | void project(const Template &src, Template &dst) const | 33 | void project(const Template &src, Template &dst) const |
| 18 | { | 34 | { |
| 19 | - dst.file = src.file; | ||
| 20 | - | ||
| 21 | - foreach (const Mat &m, src) { | ||
| 22 | - Mat padded = padValue * Mat::ones(m.rows + 2*padSize, m.cols + 2*padSize, m.type()); | ||
| 23 | - padded(Rect(padSize, padSize, padded.cols - padSize, padded.rows - padSize)) = m; | ||
| 24 | - dst += padded; | ||
| 25 | - } | 35 | + int top, bottom, left, right; |
| 36 | + top = percent*src.m().rows; bottom = percent*src.m().rows; | ||
| 37 | + left = percent*src.m().cols; right = percent*src.m().cols; | ||
| 38 | + copyMakeBorder(src, dst, top, bottom, left, right, border, Scalar(value)); | ||
| 26 | } | 39 | } |
| 27 | }; | 40 | }; |
| 28 | 41 |
openbr/plugins/imgproc/resize.cpp
| @@ -55,9 +55,15 @@ private: | @@ -55,9 +55,15 @@ private: | ||
| 55 | 55 | ||
| 56 | void project(const Template &src, Template &dst) const | 56 | void project(const Template &src, Template &dst) const |
| 57 | { | 57 | { |
| 58 | - if (!preserveAspect) | 58 | + if (!preserveAspect) { |
| 59 | resize(src, dst, Size((columns == -1) ? src.m().cols*rows/src.m().rows : columns, rows), 0, 0, method); | 59 | resize(src, dst, Size((columns == -1) ? src.m().cols*rows/src.m().rows : columns, rows), 0, 0, method); |
| 60 | - else { | 60 | + const float rowScaleFactor = (float)rows/src.m().rows; |
| 61 | + const float colScaleFactor = (float)columns/src.m().cols; | ||
| 62 | + QList<QPointF> points = src.file.points(); | ||
| 63 | + for (int i=0; i<points.size(); i++) | ||
| 64 | + points[i] = QPointF(points[i].x() * colScaleFactor,points[i].y() * rowScaleFactor); | ||
| 65 | + dst.file.setPoints(points); | ||
| 66 | + } else { | ||
| 61 | float inRatio = (float) src.m().rows / src.m().cols; | 67 | float inRatio = (float) src.m().rows / src.m().cols; |
| 62 | float outRatio = (float) rows / columns; | 68 | float outRatio = (float) rows / columns; |
| 63 | dst = Mat::zeros(rows, columns, src.m().type()); | 69 | dst = Mat::zeros(rows, columns, src.m().type()); |
openbr/plugins/imgproc/rndrotate.cpp
| @@ -16,6 +16,7 @@ | @@ -16,6 +16,7 @@ | ||
| 16 | 16 | ||
| 17 | #include <opencv2/imgproc/imgproc.hpp> | 17 | #include <opencv2/imgproc/imgproc.hpp> |
| 18 | #include <openbr/plugins/openbr_internal.h> | 18 | #include <openbr/plugins/openbr_internal.h> |
| 19 | +#include <openbr/core/opencvutils.h> | ||
| 19 | 20 | ||
| 20 | using namespace cv; | 21 | using namespace cv; |
| 21 | 22 | ||
| @@ -32,12 +33,14 @@ class RndRotateTransform : public UntrainableTransform | @@ -32,12 +33,14 @@ class RndRotateTransform : public UntrainableTransform | ||
| 32 | Q_OBJECT | 33 | Q_OBJECT |
| 33 | 34 | ||
| 34 | Q_PROPERTY(QList<int> range READ get_range WRITE set_range RESET reset_range STORED false) | 35 | Q_PROPERTY(QList<int> range READ get_range WRITE set_range RESET reset_range STORED false) |
| 36 | + Q_PROPERTY(int center READ get_center WRITE set_center RESET reset_center STORED false) | ||
| 35 | BR_PROPERTY(QList<int>, range, QList<int>() << -15 << 15) | 37 | BR_PROPERTY(QList<int>, range, QList<int>() << -15 << 15) |
| 38 | + BR_PROPERTY(int, center, -1) | ||
| 36 | 39 | ||
| 37 | void project(const Template &src, Template &dst) const { | 40 | void project(const Template &src, Template &dst) const { |
| 38 | int span = range.first() - range.last(); | 41 | int span = range.first() - range.last(); |
| 39 | - int angle = (rand() % span) + range.first(); | ||
| 40 | - Mat rotMatrix = getRotationMatrix2D(Point2f(src.m().rows/2,src.m().cols/2),angle,1.0); | 42 | + int angle = span == 0 ? range.first() : (rand() % span) + range.first(); |
| 43 | + Mat rotMatrix = getRotationMatrix2D(center == -1 ? Point2f(src.m().rows/2,src.m().cols/2) : OpenCVUtils::toPoint(src.file.points()[center]),angle,1.0); | ||
| 41 | warpAffine(src,dst,rotMatrix,Size(src.m().cols,src.m().rows),INTER_LINEAR,BORDER_REFLECT_101); | 44 | warpAffine(src,dst,rotMatrix,Size(src.m().cols,src.m().rows),INTER_LINEAR,BORDER_REFLECT_101); |
| 42 | 45 | ||
| 43 | QList<QPointF> points = src.file.points(); | 46 | QList<QPointF> points = src.file.points(); |
openbr/plugins/imgproc/roi.cpp
| @@ -34,8 +34,10 @@ class ROITransform : public UntrainableTransform | @@ -34,8 +34,10 @@ class ROITransform : public UntrainableTransform | ||
| 34 | Q_OBJECT | 34 | Q_OBJECT |
| 35 | Q_PROPERTY(QString propName READ get_propName WRITE set_propName RESET reset_propName STORED false) | 35 | Q_PROPERTY(QString propName READ get_propName WRITE set_propName RESET reset_propName STORED false) |
| 36 | Q_PROPERTY(bool copyOnCrop READ get_copyOnCrop WRITE set_copyOnCrop RESET reset_copyOnCrop STORED false) | 36 | Q_PROPERTY(bool copyOnCrop READ get_copyOnCrop WRITE set_copyOnCrop RESET reset_copyOnCrop STORED false) |
| 37 | + Q_PROPERTY(int shiftPoints READ get_shiftPoints WRITE set_shiftPoints RESET reset_shiftPoints STORED false) | ||
| 37 | BR_PROPERTY(QString, propName, "") | 38 | BR_PROPERTY(QString, propName, "") |
| 38 | BR_PROPERTY(bool, copyOnCrop, false) | 39 | BR_PROPERTY(bool, copyOnCrop, false) |
| 40 | + BR_PROPERTY(int, shiftPoints, -1) | ||
| 39 | 41 | ||
| 40 | void project(const Template &src, Template &dst) const | 42 | void project(const Template &src, Template &dst) const |
| 41 | { | 43 | { |
| @@ -55,6 +57,16 @@ class ROITransform : public UntrainableTransform | @@ -55,6 +57,16 @@ class ROITransform : public UntrainableTransform | ||
| 55 | if (Globals->verbose) | 57 | if (Globals->verbose) |
| 56 | qWarning("No rects present in file."); | 58 | qWarning("No rects present in file."); |
| 57 | } | 59 | } |
| 60 | + | ||
| 61 | + if (shiftPoints != -1) { | ||
| 62 | + // Shift the points to the rect with the index provided | ||
| 63 | + QRectF rect = src.file.rects()[shiftPoints]; | ||
| 64 | + QList<QPointF> points = src.file.points(); | ||
| 65 | + for (int i=0; i<points.size(); i++) | ||
| 66 | + points[i] -= rect.topLeft(); | ||
| 67 | + dst.file.setPoints(points); | ||
| 68 | + } | ||
| 69 | + | ||
| 58 | dst.file.clearRects(); | 70 | dst.file.clearRects(); |
| 59 | 71 | ||
| 60 | if (copyOnCrop) | 72 | if (copyOnCrop) |
openbr/plugins/imgproc/slidingwindow.cpp
| @@ -17,7 +17,6 @@ | @@ -17,7 +17,6 @@ | ||
| 17 | #include <openbr/plugins/openbr_internal.h> | 17 | #include <openbr/plugins/openbr_internal.h> |
| 18 | #include <openbr/core/opencvutils.h> | 18 | #include <openbr/core/opencvutils.h> |
| 19 | #include <openbr/core/qtutils.h> | 19 | #include <openbr/core/qtutils.h> |
| 20 | -#include <opencv2/highgui/highgui.hpp> | ||
| 21 | 20 | ||
| 22 | #include <opencv2/imgproc/imgproc.hpp> | 21 | #include <opencv2/imgproc/imgproc.hpp> |
| 23 | 22 | ||
| @@ -35,7 +34,6 @@ namespace br | @@ -35,7 +34,6 @@ namespace br | ||
| 35 | * \br_property int minSize The smallest sized object to detect in pixels | 34 | * \br_property int minSize The smallest sized object to detect in pixels |
| 36 | * \br_property int maxSize The largest sized object to detect in pixels. A negative value will set maxSize == image size | 35 | * \br_property int maxSize The largest sized object to detect in pixels. A negative value will set maxSize == image size |
| 37 | * \br_property float scaleFactor The factor to scale the image by during each resize. | 36 | * \br_property float scaleFactor The factor to scale the image by during each resize. |
| 38 | - * \br_property int minNeighbors Parameter for non-maximum supression | ||
| 39 | * \br_property float confidenceThreshold A threshold for positive detections. Positive detections returned by the classifier that have confidences below this threshold are considered negative detections. | 37 | * \br_property float confidenceThreshold A threshold for positive detections. Positive detections returned by the classifier that have confidences below this threshold are considered negative detections. |
| 40 | * \br_property float eps Parameter for non-maximum supression | 38 | * \br_property float eps Parameter for non-maximum supression |
| 41 | */ | 39 | */ |
| @@ -50,6 +48,7 @@ class SlidingWindowTransform : public MetaTransform | @@ -50,6 +48,7 @@ class SlidingWindowTransform : public MetaTransform | ||
| 50 | Q_PROPERTY(float scaleFactor READ get_scaleFactor WRITE set_scaleFactor RESET reset_scaleFactor STORED false) | 48 | Q_PROPERTY(float scaleFactor READ get_scaleFactor WRITE set_scaleFactor RESET reset_scaleFactor STORED false) |
| 51 | Q_PROPERTY(float confidenceThreshold READ get_confidenceThreshold WRITE set_confidenceThreshold RESET reset_confidenceThreshold STORED false) | 49 | Q_PROPERTY(float confidenceThreshold READ get_confidenceThreshold WRITE set_confidenceThreshold RESET reset_confidenceThreshold STORED false) |
| 52 | Q_PROPERTY(float eps READ get_eps WRITE set_eps RESET reset_eps STORED false) | 50 | Q_PROPERTY(float eps READ get_eps WRITE set_eps RESET reset_eps STORED false) |
| 51 | + Q_PROPERTY(float minNeighbors READ get_minNeighbors WRITE set_minNeighbors RESET reset_minNeighbors STORED false) | ||
| 53 | 52 | ||
| 54 | BR_PROPERTY(br::Classifier*, classifier, NULL) | 53 | BR_PROPERTY(br::Classifier*, classifier, NULL) |
| 55 | BR_PROPERTY(int, minSize, 20) | 54 | BR_PROPERTY(int, minSize, 20) |
| @@ -57,10 +56,11 @@ class SlidingWindowTransform : public MetaTransform | @@ -57,10 +56,11 @@ class SlidingWindowTransform : public MetaTransform | ||
| 57 | BR_PROPERTY(float, scaleFactor, 1.2) | 56 | BR_PROPERTY(float, scaleFactor, 1.2) |
| 58 | BR_PROPERTY(float, confidenceThreshold, 10) | 57 | BR_PROPERTY(float, confidenceThreshold, 10) |
| 59 | BR_PROPERTY(float, eps, 0.2) | 58 | BR_PROPERTY(float, eps, 0.2) |
| 59 | + BR_PROPERTY(int, minNeighbors, 3) | ||
| 60 | 60 | ||
| 61 | void train(const TemplateList &data) | 61 | void train(const TemplateList &data) |
| 62 | { | 62 | { |
| 63 | - classifier->train(data.data(), File::get<float>(data, "Label", -1)); | 63 | + classifier->train(data); |
| 64 | } | 64 | } |
| 65 | 65 | ||
| 66 | void project(const Template &src, Template &dst) const | 66 | void project(const Template &src, Template &dst) const |
| @@ -123,15 +123,18 @@ class SlidingWindowTransform : public MetaTransform | @@ -123,15 +123,18 @@ class SlidingWindowTransform : public MetaTransform | ||
| 123 | 123 | ||
| 124 | Mat scaledImage(scaledImageSize, CV_8U, imageBuffer.data); | 124 | Mat scaledImage(scaledImageSize, CV_8U, imageBuffer.data); |
| 125 | resize(m, scaledImage, scaledImageSize, 0, 0, CV_INTER_LINEAR); | 125 | resize(m, scaledImage, scaledImageSize, 0, 0, CV_INTER_LINEAR); |
| 126 | - Mat repImage = classifier->preprocess(scaledImage); | 126 | + |
| 127 | + Template repImage(t.file, scaledImage); | ||
| 128 | + repImage = classifier->preprocess(repImage); | ||
| 127 | 129 | ||
| 128 | int step = factor > 2. ? 1 : 2; | 130 | int step = factor > 2. ? 1 : 2; |
| 129 | for (int y = 0; y < processingRectSize.height; y += step) { | 131 | for (int y = 0; y < processingRectSize.height; y += step) { |
| 130 | for (int x = 0; x < processingRectSize.width; x += step) { | 132 | for (int x = 0; x < processingRectSize.width; x += step) { |
| 131 | - Mat window = repImage(Rect(Point(x, y), Size(originalWindowSize.width + dx, originalWindowSize.height + dy))).clone(); | 133 | + Mat window = repImage.m()(Rect(Point(x, y), Size(originalWindowSize.width + dx, originalWindowSize.height + dy))).clone(); |
| 134 | + Template t(window); | ||
| 132 | 135 | ||
| 133 | float confidence = 0; | 136 | float confidence = 0; |
| 134 | - int result = classifier->classify(window, false, &confidence); | 137 | + int result = classifier->classify(t, false, &confidence); |
| 135 | 138 | ||
| 136 | if (result == 1) { | 139 | if (result == 1) { |
| 137 | rects.append(Rect(cvRound(x*factor), cvRound(y*factor), windowSize.width, windowSize.height)); | 140 | rects.append(Rect(cvRound(x*factor), cvRound(y*factor), windowSize.width, windowSize.height)); |
| @@ -146,7 +149,7 @@ class SlidingWindowTransform : public MetaTransform | @@ -146,7 +149,7 @@ class SlidingWindowTransform : public MetaTransform | ||
| 146 | } | 149 | } |
| 147 | } | 150 | } |
| 148 | 151 | ||
| 149 | - OpenCVUtils::group(rects, confidences, confidenceThreshold, eps); | 152 | + OpenCVUtils::group(rects, confidences, confidenceThreshold, minNeighbors, eps); |
| 150 | 153 | ||
| 151 | if (!enrollAll && rects.empty()) { | 154 | if (!enrollAll && rects.empty()) { |
| 152 | rects.append(Rect(0, 0, m.cols, m.rows)); | 155 | rects.append(Rect(0, 0, m.cols, m.rows)); |
openbr/plugins/metadata/averagepoints.cpp
| @@ -14,23 +14,14 @@ class AveragePointsTransform : public UntrainableMetadataTransform | @@ -14,23 +14,14 @@ class AveragePointsTransform : public UntrainableMetadataTransform | ||
| 14 | Q_PROPERTY(QList<int> indices READ get_indices WRITE set_indices RESET reset_indices STORED false) | 14 | Q_PROPERTY(QList<int> indices READ get_indices WRITE set_indices RESET reset_indices STORED false) |
| 15 | Q_PROPERTY(QString metaName READ get_metaName WRITE set_metaName RESET reset_metaName STORED true) | 15 | Q_PROPERTY(QString metaName READ get_metaName WRITE set_metaName RESET reset_metaName STORED true) |
| 16 | Q_PROPERTY(bool append READ get_append WRITE set_append RESET reset_append STORED true) | 16 | Q_PROPERTY(bool append READ get_append WRITE set_append RESET reset_append STORED true) |
| 17 | - Q_PROPERTY(int nLandmarks READ get_nLandmarks WRITE set_nLandmarks RESET reset_nLandmarks STORED true) | ||
| 18 | BR_PROPERTY(QList<int>, indices, QList<int>()) | 17 | BR_PROPERTY(QList<int>, indices, QList<int>()) |
| 19 | BR_PROPERTY(QString, metaName, "") | 18 | BR_PROPERTY(QString, metaName, "") |
| 20 | BR_PROPERTY(bool, append, false) | 19 | BR_PROPERTY(bool, append, false) |
| 21 | - BR_PROPERTY(int, nLandmarks, 51) | ||
| 22 | 20 | ||
| 23 | void projectMetadata(const File &src, File &dst) const | 21 | void projectMetadata(const File &src, File &dst) const |
| 24 | { | 22 | { |
| 25 | dst = src; | 23 | dst = src; |
| 26 | - if (src.points().size() != nLandmarks) { | ||
| 27 | - if (Globals->verbose) | ||
| 28 | - qDebug() << "Warning: Face has " << src.points().size() << "points; should be " << nLandmarks; | ||
| 29 | - dst.fte = true; | ||
| 30 | - return; | ||
| 31 | - } | ||
| 32 | - int x1 = 0, | ||
| 33 | - y1 = 0; | 24 | + float x1 = 0, y1 = 0; |
| 34 | 25 | ||
| 35 | for (int i = 0; i < indices.size(); i++) { | 26 | for (int i = 0; i < indices.size(); i++) { |
| 36 | x1 += src.points()[indices[i]].x(); | 27 | x1 += src.points()[indices[i]].x(); |
openbr/plugins/metadata/cascade.cpp
| @@ -202,7 +202,8 @@ class CascadeTransform : public MetaTransform | @@ -202,7 +202,8 @@ class CascadeTransform : public MetaTransform | ||
| 202 | Q_PROPERTY(int minSize READ get_minSize WRITE set_minSize RESET reset_minSize STORED false) | 202 | Q_PROPERTY(int minSize READ get_minSize WRITE set_minSize RESET reset_minSize STORED false) |
| 203 | Q_PROPERTY(int minNeighbors READ get_minNeighbors WRITE set_minNeighbors RESET reset_minNeighbors STORED false) | 203 | Q_PROPERTY(int minNeighbors READ get_minNeighbors WRITE set_minNeighbors RESET reset_minNeighbors STORED false) |
| 204 | Q_PROPERTY(bool ROCMode READ get_ROCMode WRITE set_ROCMode RESET reset_ROCMode STORED false) | 204 | Q_PROPERTY(bool ROCMode READ get_ROCMode WRITE set_ROCMode RESET reset_ROCMode STORED false) |
| 205 | - | 205 | + Q_PROPERTY(float scaleFactor READ get_scaleFactor WRITE set_scaleFactor RESET reset_scaleFactor STORED false) |
| 206 | + | ||
| 206 | // Training parameters | 207 | // Training parameters |
| 207 | Q_PROPERTY(int numStages READ get_numStages WRITE set_numStages RESET reset_numStages STORED false) | 208 | Q_PROPERTY(int numStages READ get_numStages WRITE set_numStages RESET reset_numStages STORED false) |
| 208 | Q_PROPERTY(int w READ get_w WRITE set_w RESET reset_w STORED false) | 209 | Q_PROPERTY(int w READ get_w WRITE set_w RESET reset_w STORED false) |
| @@ -227,7 +228,8 @@ class CascadeTransform : public MetaTransform | @@ -227,7 +228,8 @@ class CascadeTransform : public MetaTransform | ||
| 227 | BR_PROPERTY(int, minSize, 64) | 228 | BR_PROPERTY(int, minSize, 64) |
| 228 | BR_PROPERTY(int, minNeighbors, 5) | 229 | BR_PROPERTY(int, minNeighbors, 5) |
| 229 | BR_PROPERTY(bool, ROCMode, false) | 230 | BR_PROPERTY(bool, ROCMode, false) |
| 230 | - | 231 | + BR_PROPERTY(float, scaleFactor, 1.2) |
| 232 | + | ||
| 231 | // Training parameters - Default values provided trigger OpenCV defaults | 233 | // Training parameters - Default values provided trigger OpenCV defaults |
| 232 | BR_PROPERTY(int, numStages, -1) | 234 | BR_PROPERTY(int, numStages, -1) |
| 233 | BR_PROPERTY(int, w, -1) | 235 | BR_PROPERTY(int, w, -1) |
| @@ -410,8 +412,19 @@ class CascadeTransform : public MetaTransform | @@ -410,8 +412,19 @@ class CascadeTransform : public MetaTransform | ||
| 410 | std::vector<Rect> rects; | 412 | std::vector<Rect> rects; |
| 411 | std::vector<int> rejectLevels; | 413 | std::vector<int> rejectLevels; |
| 412 | std::vector<double> levelWeights; | 414 | std::vector<double> levelWeights; |
| 413 | - if (ROCMode) cascade->detectMultiScale(m, rects, rejectLevels, levelWeights, 1.2, minNeighbors, flags | CASCADE_SCALE_IMAGE, Size(minSize, minSize), Size(), true); | ||
| 414 | - else cascade->detectMultiScale(m, rects, 1.2, minNeighbors, flags, Size(minSize, minSize)); | 415 | + if (ROCMode) cascade->detectMultiScale(m, rects, rejectLevels, levelWeights, scaleFactor, minNeighbors, flags | CASCADE_SCALE_IMAGE, Size(minSize, minSize), Size(), true); |
| 416 | + else cascade->detectMultiScale(m, rects, scaleFactor, minNeighbors, flags, Size(minSize, minSize)); | ||
| 417 | + | ||
| 418 | + // It appears that flags is ignored for new model files: | ||
| 419 | + // http://docs.opencv.org/modules/objdetect/doc/cascade_classification.html#cascadeclassifier-detectmultiscale | ||
| 420 | + if ((flags == CASCADE_FIND_BIGGEST_OBJECT) && (rects.size() > 1)) { | ||
| 421 | + Rect biggest = rects[0]; | ||
| 422 | + for (size_t j=0; j<rects.size(); j++) | ||
| 423 | + if (rects[j].area() > biggest.area()) | ||
| 424 | + biggest = rects[j]; | ||
| 425 | + rects.clear(); | ||
| 426 | + rects.push_back(biggest); | ||
| 427 | + } | ||
| 415 | 428 | ||
| 416 | bool empty = false; | 429 | bool empty = false; |
| 417 | if (!enrollAll && rects.empty()) { | 430 | if (!enrollAll && rects.empty()) { |
| @@ -426,7 +439,7 @@ class CascadeTransform : public MetaTransform | @@ -426,7 +439,7 @@ class CascadeTransform : public MetaTransform | ||
| 426 | u.file.set("Confidence",-std::numeric_limits<float>::max()); | 439 | u.file.set("Confidence",-std::numeric_limits<float>::max()); |
| 427 | } else if (rejectLevels.size() > j) | 440 | } else if (rejectLevels.size() > j) |
| 428 | u.file.set("Confidence", rejectLevels[j]*levelWeights[j]); | 441 | u.file.set("Confidence", rejectLevels[j]*levelWeights[j]); |
| 429 | - else | 442 | + else |
| 430 | u.file.set("Confidence", rects[j].area()); | 443 | u.file.set("Confidence", rects[j].area()); |
| 431 | const QRectF rect = OpenCVUtils::fromRect(rects[j]); | 444 | const QRectF rect = OpenCVUtils::fromRect(rects[j]); |
| 432 | u.file.appendRect(rect); | 445 | u.file.appendRect(rect); |
openbr/plugins/metadata/keytorect.cpp renamed to openbr/plugins/metadata/keytolandmark.cpp
| @@ -24,28 +24,35 @@ namespace br | @@ -24,28 +24,35 @@ namespace br | ||
| 24 | * \brief Convert values of key_X, key_Y, key_Width, key_Height to a rect. | 24 | * \brief Convert values of key_X, key_Y, key_Width, key_Height to a rect. |
| 25 | * \author Jordan Cheney \cite JordanCheney | 25 | * \author Jordan Cheney \cite JordanCheney |
| 26 | */ | 26 | */ |
| 27 | -class KeyToRectTransform : public UntrainableMetadataTransform | 27 | +class KeyToLandmarkTransform : public UntrainableMetadataTransform |
| 28 | { | 28 | { |
| 29 | Q_OBJECT | 29 | Q_OBJECT |
| 30 | Q_PROPERTY(QString key READ get_key WRITE set_key RESET reset_key STORED false) | 30 | Q_PROPERTY(QString key READ get_key WRITE set_key RESET reset_key STORED false) |
| 31 | + Q_PROPERTY(bool point READ get_point WRITE set_point RESET reset_point STORED false) | ||
| 31 | BR_PROPERTY(QString, key, "") | 32 | BR_PROPERTY(QString, key, "") |
| 33 | + BR_PROPERTY(bool, point, false) | ||
| 32 | 34 | ||
| 33 | void projectMetadata(const File &src, File &dst) const | 35 | void projectMetadata(const File &src, File &dst) const |
| 34 | { | 36 | { |
| 35 | dst = src; | 37 | dst = src; |
| 36 | 38 | ||
| 37 | - if (src.contains(QStringList() << key + "_X" << key + "_Y" << key + "_Width" << key + "_Height")) | ||
| 38 | - dst.appendRect(QRectF(src.get<int>(key + "_X"), | ||
| 39 | - src.get<int>(key + "_Y"), | ||
| 40 | - src.get<int>(key + "_Width"), | ||
| 41 | - src.get<int>(key + "_Height"))); | 39 | + if (point) { |
| 40 | + if (src.contains(QStringList() << key + "_X" << key + "_Y")) | ||
| 41 | + dst.appendPoint(QPointF(src.get<float>(key + "_X"), | ||
| 42 | + src.get<float>(key + "_Y"))); | ||
| 43 | + } else { | ||
| 44 | + if (src.contains(QStringList() << key + "_X" << key + "_Y" << key + "_Width" << key + "_Height")) | ||
| 45 | + dst.appendRect(QRectF(src.get<float>(key + "_X"), | ||
| 46 | + src.get<float>(key + "_Y"), | ||
| 47 | + src.get<float>(key + "_Width"), | ||
| 48 | + src.get<float>(key + "_Height"))); | ||
| 49 | + } | ||
| 42 | 50 | ||
| 43 | } | 51 | } |
| 44 | - | ||
| 45 | }; | 52 | }; |
| 46 | 53 | ||
| 47 | -BR_REGISTER(Transform, KeyToRectTransform) | 54 | +BR_REGISTER(Transform, KeyToLandmarkTransform) |
| 48 | 55 | ||
| 49 | } // namespace br | 56 | } // namespace br |
| 50 | 57 | ||
| 51 | -#include "metadata/keytorect.moc" | 58 | +#include "metadata/keytolandmark.moc" |
openbr/plugins/metadata/removetemplates.cpp
| @@ -31,15 +31,22 @@ class RemoveTemplatesTransform : public UntrainableMetaTransform | @@ -31,15 +31,22 @@ class RemoveTemplatesTransform : public UntrainableMetaTransform | ||
| 31 | Q_OBJECT | 31 | Q_OBJECT |
| 32 | Q_PROPERTY(QString regexp READ get_regexp WRITE set_regexp RESET reset_regexp STORED false) | 32 | Q_PROPERTY(QString regexp READ get_regexp WRITE set_regexp RESET reset_regexp STORED false) |
| 33 | Q_PROPERTY(QString key READ get_key WRITE set_key RESET reset_key STORED false) | 33 | Q_PROPERTY(QString key READ get_key WRITE set_key RESET reset_key STORED false) |
| 34 | + Q_PROPERTY(bool keep READ get_keep WRITE set_keep RESET reset_keep STORED false) | ||
| 34 | BR_PROPERTY(QString, regexp, "") | 35 | BR_PROPERTY(QString, regexp, "") |
| 35 | BR_PROPERTY(QString, key, "") | 36 | BR_PROPERTY(QString, key, "") |
| 37 | + BR_PROPERTY(bool, keep, false) | ||
| 36 | 38 | ||
| 37 | void project(const Template &src, Template &dst) const | 39 | void project(const Template &src, Template &dst) const |
| 38 | { | 40 | { |
| 39 | - const QRegularExpression re(regexp); | ||
| 40 | - const QRegularExpressionMatch match = re.match(key.isEmpty() ? src.file.suffix() : src.file.get<QString>(key)); | ||
| 41 | - if (match.hasMatch()) dst = Template(); | ||
| 42 | - else dst = src; | 41 | + dst = src; |
| 42 | + QRegExp re(regexp); | ||
| 43 | + re.setPatternSyntax(QRegExp::Wildcard); | ||
| 44 | + bool match = re.exactMatch(key.isEmpty() ? src.file.suffix() : src.file.get<QString>(key)); | ||
| 45 | + | ||
| 46 | + if (keep && !match) | ||
| 47 | + dst.file.fte = true; | ||
| 48 | + else if (!keep && match) | ||
| 49 | + dst.file.fte = true; | ||
| 43 | } | 50 | } |
| 44 | }; | 51 | }; |
| 45 | 52 |
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,18 +57,18 @@ BR_REGISTER(Initializer, StasmInitializer) | @@ -56,18 +57,18 @@ 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) |
| 70 | BR_PROPERTY(QStringList, pinLabels, QStringList()) | 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 | Resource<StasmCascadeClassifier> stasmCascadeResource; | 73 | Resource<StasmCascadeClassifier> stasmCascadeResource; |
| 73 | 74 | ||
| @@ -77,86 +78,119 @@ class StasmTransform : public UntrainableTransform | @@ -77,86 +78,119 @@ class StasmTransform : public UntrainableTransform | ||
| 77 | stasmCascadeResource.setResourceMaker(new StasmResourceMaker()); | 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 | }; |
openbr/plugins/output/knn.cpp
| @@ -6,44 +6,80 @@ | @@ -6,44 +6,80 @@ | ||
| 6 | namespace br | 6 | namespace br |
| 7 | { | 7 | { |
| 8 | 8 | ||
| 9 | +typedef QPair<float,int> Pair; | ||
| 10 | + | ||
| 9 | /*! | 11 | /*! |
| 10 | * \ingroup outputs | 12 | * \ingroup outputs |
| 11 | * \brief Outputs the k-Nearest Neighbors from the gallery for each probe. | 13 | * \brief Outputs the k-Nearest Neighbors from the gallery for each probe. |
| 12 | * \author Ben Klein \cite bhklein | 14 | * \author Ben Klein \cite bhklein |
| 13 | */ | 15 | */ |
| 14 | -class knnOutput : public MatrixOutput | 16 | +class knnOutput : public Output |
| 15 | { | 17 | { |
| 16 | Q_OBJECT | 18 | Q_OBJECT |
| 17 | 19 | ||
| 20 | + int rowBlock, columnBlock; | ||
| 21 | + size_t headerSize, k; | ||
| 22 | + cv::Mat blockScores; | ||
| 23 | + | ||
| 18 | ~knnOutput() | 24 | ~knnOutput() |
| 19 | { | 25 | { |
| 20 | - size_t num_probes = (size_t)queryFiles.size(); | ||
| 21 | - if (targetFiles.isEmpty() || queryFiles.isEmpty()) return; | ||
| 22 | - size_t k = file.get<size_t>("k", 20); | 26 | + writeBlock(); |
| 27 | + } | ||
| 23 | 28 | ||
| 24 | - if ((size_t)targetFiles.size() < k) | ||
| 25 | - qFatal("Gallery size %s is smaller than k = %s.", qPrintable(QString::number(targetFiles.size())), qPrintable(QString::number(k))); | 29 | + void setBlock(int rowBlock, int columnBlock) |
| 30 | + { | ||
| 31 | + if ((rowBlock == 0) && (columnBlock == 0)) { | ||
| 32 | + k = file.get<size_t>("k", 20); | ||
| 33 | + QFile f(file); | ||
| 34 | + if (!f.open(QFile::WriteOnly)) | ||
| 35 | + qFatal("Unable to open %s for writing.", qPrintable(file)); | ||
| 36 | + size_t querySize = (size_t)queryFiles.size(); | ||
| 37 | + f.write((const char*) &querySize, sizeof(size_t)); | ||
| 38 | + f.write((const char*) &k, sizeof(size_t)); | ||
| 39 | + headerSize = 2 * sizeof(size_t); | ||
| 40 | + } else { | ||
| 41 | + writeBlock(); | ||
| 42 | + } | ||
| 26 | 43 | ||
| 27 | - QFile f(file); | ||
| 28 | - if (!f.open(QFile::WriteOnly)) | ||
| 29 | - qFatal("Unable to open %s for writing.", qPrintable(file)); | ||
| 30 | - f.write((const char*) &num_probes, sizeof(size_t)); | ||
| 31 | - f.write((const char*) &k, sizeof(size_t)); | 44 | + this->rowBlock = rowBlock; |
| 45 | + this->columnBlock = columnBlock; | ||
| 46 | + | ||
| 47 | + int matrixRows = std::min(queryFiles.size()-rowBlock*this->blockRows, blockRows); | ||
| 48 | + int matrixCols = std::min(targetFiles.size()-columnBlock*this->blockCols, blockCols); | ||
| 49 | + | ||
| 50 | + blockScores = cv::Mat(matrixRows, matrixCols, CV_32FC1); | ||
| 51 | + } | ||
| 32 | 52 | ||
| 33 | - QVector<Candidate> neighbors; neighbors.reserve(num_probes*k); | 53 | + void setRelative(float value, int i, int j) |
| 54 | + { | ||
| 55 | + blockScores.at<float>(i,j) = value; | ||
| 56 | + } | ||
| 57 | + | ||
| 58 | + void set(float value, int i, int j) | ||
| 59 | + { | ||
| 60 | + (void) value; (void) i; (void) j; | ||
| 61 | + qFatal("Logic error."); | ||
| 62 | + } | ||
| 63 | + | ||
| 64 | + void writeBlock() | ||
| 65 | + { | ||
| 66 | + QFile f(file); | ||
| 67 | + if (!f.open(QFile::ReadWrite)) | ||
| 68 | + qFatal("Unable to open %s for modifying.", qPrintable(file)); | ||
| 69 | + QVector<Candidate> neighbors; neighbors.reserve(k * blockScores.rows); | ||
| 34 | 70 | ||
| 35 | - for (size_t i=0; i<num_probes; i++) { | ||
| 36 | - typedef QPair<float,int> Pair; | 71 | + for (int i=0; i<blockScores.rows; i++) { |
| 37 | size_t rank = 0; | 72 | size_t rank = 0; |
| 38 | - foreach (const Pair &pair, Common::Sort(OpenCVUtils::matrixToVector<float>(data.row(i)), true)) { | ||
| 39 | - if (QString(targetFiles[pair.second]) != QString(queryFiles[i])) { | 73 | + foreach (const Pair &pair, Common::Sort(OpenCVUtils::matrixToVector<float>(blockScores.row(i)), true)) { |
| 74 | + if (QString(targetFiles[pair.second]) != QString(queryFiles[rowBlock*this->blockRows+i])) { | ||
| 40 | Candidate candidate((size_t)pair.second, pair.first); | 75 | Candidate candidate((size_t)pair.second, pair.first); |
| 41 | neighbors.push_back(candidate); | 76 | neighbors.push_back(candidate); |
| 42 | if (++rank >= k) break; | 77 | if (++rank >= k) break; |
| 43 | } | 78 | } |
| 44 | } | 79 | } |
| 45 | } | 80 | } |
| 46 | - f.write((const char*) neighbors.data(), num_probes * k * sizeof(Candidate)); | 81 | + f.seek(headerSize + sizeof(Candidate)*quint64(rowBlock*this->blockRows)*k); |
| 82 | + f.write((const char*) neighbors.data(), blockScores.rows * k * sizeof(Candidate)); | ||
| 47 | f.close(); | 83 | f.close(); |
| 48 | } | 84 | } |
| 49 | }; | 85 | }; |
openbr/plugins/representation/haar.cpp
| @@ -80,23 +80,25 @@ class HaarRepresentation : public Representation | @@ -80,23 +80,25 @@ class HaarRepresentation : public Representation | ||
| 80 | } | 80 | } |
| 81 | } | 81 | } |
| 82 | 82 | ||
| 83 | - void preprocess(const Mat &src, Mat &dst) const | 83 | + Template preprocess(const Template &src) const |
| 84 | { | 84 | { |
| 85 | + Template dst; | ||
| 85 | integral(src, dst); | 86 | integral(src, dst); |
| 87 | + return dst; | ||
| 86 | } | 88 | } |
| 87 | 89 | ||
| 88 | - float evaluate(const Mat &image, int idx) const | 90 | + float evaluate(const Template &src, int idx) const |
| 89 | { | 91 | { |
| 90 | - return (float)features[idx].calc(image); | 92 | + return (float)features[idx].calc(src.m()); |
| 91 | } | 93 | } |
| 92 | 94 | ||
| 93 | - Mat evaluate(const Mat &image, const QList<int> &indices) const | 95 | + Mat evaluate(const Template &src, const QList<int> &indices) const |
| 94 | { | 96 | { |
| 95 | int size = indices.empty() ? numFeatures() : indices.size(); | 97 | int size = indices.empty() ? numFeatures() : indices.size(); |
| 96 | 98 | ||
| 97 | Mat result(1, size, CV_32FC1); | 99 | Mat result(1, size, CV_32FC1); |
| 98 | for (int i = 0; i < size; i++) | 100 | for (int i = 0; i < size; i++) |
| 99 | - result.at<float>(i) = evaluate(image, indices.empty() ? i : indices[i]); | 101 | + result.at<float>(i) = evaluate(src, indices.empty() ? i : indices[i]); |
| 100 | return result; | 102 | return result; |
| 101 | } | 103 | } |
| 102 | 104 |
openbr/plugins/representation/mblbp.cpp
| @@ -39,32 +39,36 @@ class MBLBPRepresentation : public Representation | @@ -39,32 +39,36 @@ class MBLBPRepresentation : public Representation | ||
| 39 | 39 | ||
| 40 | void init() | 40 | void init() |
| 41 | { | 41 | { |
| 42 | - int offset = winWidth + 1; | ||
| 43 | - for (int x = 0; x < winWidth; x++ ) | ||
| 44 | - for (int y = 0; y < winHeight; y++ ) | ||
| 45 | - for (int w = 1; w <= winWidth / 3; w++ ) | ||
| 46 | - for (int h = 1; h <= winHeight / 3; h++ ) | ||
| 47 | - if ((x+3*w <= winWidth) && (y+3*h <= winHeight) ) | ||
| 48 | - features.append(Feature(offset, x, y, w, h ) ); | 42 | + if (features.isEmpty()) { |
| 43 | + int offset = winWidth + 1; | ||
| 44 | + for (int x = 0; x < winWidth; x++ ) | ||
| 45 | + for (int y = 0; y < winHeight; y++ ) | ||
| 46 | + for (int w = 1; w <= winWidth / 3; w++ ) | ||
| 47 | + for (int h = 1; h <= winHeight / 3; h++ ) | ||
| 48 | + if ((x+3*w <= winWidth) && (y+3*h <= winHeight) ) | ||
| 49 | + features.append(Feature(offset, x, y, w, h ) ); | ||
| 50 | + } | ||
| 49 | } | 51 | } |
| 50 | 52 | ||
| 51 | - void preprocess(const Mat &src, Mat &dst) const | 53 | + Template preprocess(const Template &src) const |
| 52 | { | 54 | { |
| 55 | + Template dst; | ||
| 53 | integral(src, dst); | 56 | integral(src, dst); |
| 57 | + return dst; | ||
| 54 | } | 58 | } |
| 55 | 59 | ||
| 56 | - float evaluate(const Mat &image, int idx) const | 60 | + float evaluate(const Template &src, int idx) const |
| 57 | { | 61 | { |
| 58 | - return (float)features[idx].calc(image); | 62 | + return (float)features[idx].calc(src.m()); |
| 59 | } | 63 | } |
| 60 | 64 | ||
| 61 | - Mat evaluate(const Mat &image, const QList<int> &indices) const | 65 | + Mat evaluate(const Template &src, const QList<int> &indices) const |
| 62 | { | 66 | { |
| 63 | int size = indices.empty() ? numFeatures() : indices.size(); | 67 | int size = indices.empty() ? numFeatures() : indices.size(); |
| 64 | 68 | ||
| 65 | Mat result(1, size, CV_32FC1); | 69 | Mat result(1, size, CV_32FC1); |
| 66 | for (int i = 0; i < size; i++) | 70 | for (int i = 0; i < size; i++) |
| 67 | - result.at<float>(i) = evaluate(image, indices.empty() ? i : indices[i]); | 71 | + result.at<float>(i) = evaluate(src, indices.empty() ? i : indices[i]); |
| 68 | return result; | 72 | return result; |
| 69 | } | 73 | } |
| 70 | 74 |
openbr/plugins/representation/npd.cpp
| @@ -16,23 +16,25 @@ class NPDRepresentation : public Representation | @@ -16,23 +16,25 @@ class NPDRepresentation : public Representation | ||
| 16 | 16 | ||
| 17 | void init() | 17 | void init() |
| 18 | { | 18 | { |
| 19 | - for (int p1 = 0; p1 < (winWidth * winHeight); p1++) | ||
| 20 | - for (int p2 = p1; p2 < (winWidth * winHeight); p2++) | ||
| 21 | - features.append(Feature(p1, p2)); | 19 | + if (features.isEmpty()) { |
| 20 | + for (int p1 = 0; p1 < (winWidth * winHeight); p1++) | ||
| 21 | + for (int p2 = p1; p2 < (winWidth * winHeight); p2++) | ||
| 22 | + features.append(Feature(p1, p2)); | ||
| 23 | + } | ||
| 22 | } | 24 | } |
| 23 | 25 | ||
| 24 | - float evaluate(const Mat &image, int idx) const | 26 | + float evaluate(const Template &src, int idx) const |
| 25 | { | 27 | { |
| 26 | - return features[idx].calc(image); | 28 | + return features[idx].calc(src.m()); |
| 27 | } | 29 | } |
| 28 | 30 | ||
| 29 | - Mat evaluate(const Mat &image, const QList<int> &indices) const | 31 | + Mat evaluate(const Template &src, const QList<int> &indices) const |
| 30 | { | 32 | { |
| 31 | int size = indices.empty() ? numFeatures() : indices.size(); | 33 | int size = indices.empty() ? numFeatures() : indices.size(); |
| 32 | 34 | ||
| 33 | Mat result(1, size, CV_32FC1); | 35 | Mat result(1, size, CV_32FC1); |
| 34 | for (int i = 0; i < size; i++) | 36 | for (int i = 0; i < size; i++) |
| 35 | - result.at<float>(i) = evaluate(image, indices.empty() ? i : indices[i]); | 37 | + result.at<float>(i) = evaluate(src, indices.empty() ? i : indices[i]); |
| 36 | return result; | 38 | return result; |
| 37 | } | 39 | } |
| 38 | 40 | ||
| @@ -49,10 +51,10 @@ class NPDRepresentation : public Representation | @@ -49,10 +51,10 @@ class NPDRepresentation : public Representation | ||
| 49 | struct Feature | 51 | struct Feature |
| 50 | { | 52 | { |
| 51 | Feature() {} | 53 | Feature() {} |
| 52 | - Feature(int p1, int p2) { p[0] = p1; p[1] = p2; } | 54 | + Feature(int p0, int p1) : p0(p0), p1(p1) {} |
| 53 | float calc(const Mat &image) const; | 55 | float calc(const Mat &image) const; |
| 54 | 56 | ||
| 55 | - int p[2]; | 57 | + int p0, p1; |
| 56 | }; | 58 | }; |
| 57 | QList<Feature> features; | 59 | QList<Feature> features; |
| 58 | }; | 60 | }; |
| @@ -61,9 +63,9 @@ BR_REGISTER(Representation, NPDRepresentation) | @@ -61,9 +63,9 @@ BR_REGISTER(Representation, NPDRepresentation) | ||
| 61 | 63 | ||
| 62 | inline float NPDRepresentation::Feature::calc(const Mat &image) const | 64 | inline float NPDRepresentation::Feature::calc(const Mat &image) const |
| 63 | { | 65 | { |
| 64 | - const int *ptr = image.ptr<int>(); | ||
| 65 | - int v1 = ptr[p[0]], v2 = ptr[p[1]]; | ||
| 66 | - return v1 == 0 && v2 == 0 ? 0 : ((float)(v1 - v2)) / (v1 + v2); | 66 | + const uchar *ptr = image.ptr(); |
| 67 | + int v1 = (int)ptr[p0], v2 = (int)ptr[p1]; | ||
| 68 | + return (v1 + v2) == 0 ? 0 : (1.0 * (v1 - v2)) / (v1 + v2); | ||
| 67 | } | 69 | } |
| 68 | 70 | ||
| 69 | } // namespace br | 71 | } // namespace br |
openbr/plugins/representation/random.cpp
| @@ -28,9 +28,9 @@ class RandomRepresentation : public Representation | @@ -28,9 +28,9 @@ class RandomRepresentation : public Representation | ||
| 28 | 28 | ||
| 29 | QList<int> features; | 29 | QList<int> features; |
| 30 | 30 | ||
| 31 | - void train(const QList<Mat> &images, const QList<float> &labels) | 31 | + void train(const TemplateList &data) |
| 32 | { | 32 | { |
| 33 | - representation->train(images, labels); | 33 | + representation->train(data); |
| 34 | 34 | ||
| 35 | const int nFeatures = representation->numFeatures(); | 35 | const int nFeatures = representation->numFeatures(); |
| 36 | 36 | ||
| @@ -40,17 +40,17 @@ class RandomRepresentation : public Representation | @@ -40,17 +40,17 @@ class RandomRepresentation : public Representation | ||
| 40 | features = Common::RandSample(count,nFeatures,0,true); | 40 | features = Common::RandSample(count,nFeatures,0,true); |
| 41 | } | 41 | } |
| 42 | 42 | ||
| 43 | - void preprocess(const Mat &src, Mat &dst) const | 43 | + Template preprocess(const Template &src) const |
| 44 | { | 44 | { |
| 45 | - representation->preprocess(src,dst); | 45 | + return representation->preprocess(src); |
| 46 | } | 46 | } |
| 47 | 47 | ||
| 48 | - float evaluate(const Mat &image, int idx) const | 48 | + float evaluate(const Template &src, int idx) const |
| 49 | { | 49 | { |
| 50 | - return representation->evaluate(image,features[idx]); | 50 | + return representation->evaluate(src, features[idx]); |
| 51 | } | 51 | } |
| 52 | 52 | ||
| 53 | - Mat evaluate(const Mat &image, const QList<int> &indices) const | 53 | + Mat evaluate(const Template &src, const QList<int> &indices) const |
| 54 | { | 54 | { |
| 55 | QList<int> newIndices; | 55 | QList<int> newIndices; |
| 56 | if (indices.empty()) | 56 | if (indices.empty()) |
| @@ -59,7 +59,7 @@ class RandomRepresentation : public Representation | @@ -59,7 +59,7 @@ class RandomRepresentation : public Representation | ||
| 59 | for (int i = 0; i < indices.size(); i++) | 59 | for (int i = 0; i < indices.size(); i++) |
| 60 | newIndices.append(features[indices[i]]); | 60 | newIndices.append(features[indices[i]]); |
| 61 | 61 | ||
| 62 | - return representation->evaluate(image,newIndices); | 62 | + return representation->evaluate(src, newIndices); |
| 63 | } | 63 | } |
| 64 | 64 | ||
| 65 | int numFeatures() const | 65 | int numFeatures() const |
| @@ -108,6 +108,3 @@ BR_REGISTER(Representation, RandomRepresentation) | @@ -108,6 +108,3 @@ BR_REGISTER(Representation, RandomRepresentation) | ||
| 108 | } // namespace br | 108 | } // namespace br |
| 109 | 109 | ||
| 110 | #include "representation/random.moc" | 110 | #include "representation/random.moc" |
| 111 | - | ||
| 112 | - | ||
| 113 | - |
scripts/face_cluster_viz.py renamed to scripts/brpy/face_cluster_viz.py
| 1 | #!/usr/bin/env python | 1 | #!/usr/bin/env python |
| 2 | 2 | ||
| 3 | -from PIL import Image | ||
| 4 | import csv, sys, json, argparse | 3 | import csv, sys, json, argparse |
| 4 | +from brpy.html_viz import crop_to_bb | ||
| 5 | 5 | ||
| 6 | parser = argparse.ArgumentParser(description='Visualize face cluster results in an HTML page.') | 6 | parser = argparse.ArgumentParser(description='Visualize face cluster results in an HTML page.') |
| 7 | parser.add_argument('input_file', type=str, help='Results from clustering (in csv format)') | 7 | parser.add_argument('input_file', type=str, help='Results from clustering (in csv format)') |
| @@ -16,24 +16,15 @@ clustmap = dict() | @@ -16,24 +16,15 @@ clustmap = dict() | ||
| 16 | with open(args.input_file) as f: | 16 | with open(args.input_file) as f: |
| 17 | for line in csv.DictReader(f): | 17 | for line in csv.DictReader(f): |
| 18 | c = int(line[args.cluster_key]) | 18 | c = int(line[args.cluster_key]) |
| 19 | + if c not in clustmap: | ||
| 20 | + clustmap[c] = [] | ||
| 19 | x,y,width,height = [ float(line[k]) for k in ('Face_X','Face_Y','Face_Width','Face_Height') ] | 21 | x,y,width,height = [ float(line[k]) for k in ('Face_X','Face_Y','Face_Width','Face_Height') ] |
| 20 | imname = '%s/%s' % (args.img_loc, line['File']) | 22 | imname = '%s/%s' % (args.img_loc, line['File']) |
| 21 | try: | 23 | try: |
| 22 | - img = Image.open(imname) | ||
| 23 | - imwidth, imheight = img.size | 24 | + html = crop_to_bb(x,y,width,height,imname,maxheight=400) |
| 24 | except IOError: | 25 | except IOError: |
| 25 | print('problem with %s' % imname) | 26 | print('problem with %s' % imname) |
| 26 | continue | 27 | continue |
| 27 | - ratio = maxheight / height | ||
| 28 | - # note for future me: | ||
| 29 | - # image is cropped with div width/height + overflow:hidden, | ||
| 30 | - # resized with img height, | ||
| 31 | - # and positioned with img margin | ||
| 32 | - html = '<div style="overflow:hidden;display:inline-block;width:%ipx;height:%ipx;">' % (width*ratio, maxheight) | ||
| 33 | - html += '<img src="%s" style="height:%ipx;margin:-%ipx 0 0 -%ipx;"/>' % (imname, imheight*ratio, y*ratio, x*ratio) | ||
| 34 | - html += '</div>' | ||
| 35 | - if c not in clustmap: | ||
| 36 | - clustmap[c] = [] | ||
| 37 | clustmap[c].append(html) | 28 | clustmap[c].append(html) |
| 38 | 29 | ||
| 39 | # browsers crash for a DOM with this many img tags, | 30 | # browsers crash for a DOM with this many img tags, |
scripts/brpy/html_viz.py
0 โ 100644
| 1 | +''' | ||
| 2 | +Some funcs to generate HTML visualizations. | ||
| 3 | +Run from the folder you intend to save the HTML page so | ||
| 4 | +the relative paths in the HTML file are correct and PIL can find the images on disk. | ||
| 5 | +Requires local images, but should be pretty easy to set up an apache server (or whatev) | ||
| 6 | +and host them as long as the relative paths remain the same on ya serva. | ||
| 7 | +''' | ||
| 8 | + | ||
| 9 | +from PIL import Image | ||
| 10 | + | ||
| 11 | +def crop_to_bb(x, y, width, height, imname, maxheight=None): | ||
| 12 | + ''' | ||
| 13 | + Generates an HTML string that crops to a given bounding box and resizes to maxheight pixels. | ||
| 14 | + A maxheight of None will keep the original size (default). | ||
| 15 | + When two crops are put next to each other, they will be inline. To make each crop its own line, wrap it in a div. | ||
| 16 | + ''' | ||
| 17 | + img = Image.open(imname) | ||
| 18 | + imwidth, imheight = img.size | ||
| 19 | + if not maxheight: | ||
| 20 | + maxheight = height | ||
| 21 | + ratio = maxheight / height | ||
| 22 | + # note for future me: | ||
| 23 | + # image is cropped with div width/height + overflow:hidden, | ||
| 24 | + # resized with img height, | ||
| 25 | + # and positioned with img margin | ||
| 26 | + html = '<div style="overflow:hidden; display:inline-block; width:%ipx; height:%ipx;">' % (width*ratio, maxheight) | ||
| 27 | + html += '<img src="%s" style="height:%ipx; margin:-%ipx 0 0 -%ipx;"/>' % (imname, imheight*ratio, y*ratio, x*ratio) | ||
| 28 | + html += '</div>' | ||
| 29 | + return html | ||
| 30 | + | ||
| 31 | +def bbs_for_image(imname, bbs, maxheight=None, colors=None): | ||
| 32 | + ''' | ||
| 33 | + Generates an HTML string for an image with bounding boxes. | ||
| 34 | + bbs: iterable of (x,y,width,height) bounding box tuples | ||
| 35 | + ''' | ||
| 36 | + img = Image.open(imname) | ||
| 37 | + imwidth, imheight = img.size | ||
| 38 | + if not maxheight: | ||
| 39 | + maxheight = imheight | ||
| 40 | + ratio = maxheight/imheight | ||
| 41 | + html = [ | ||
| 42 | + '<div style="position:relative">', | ||
| 43 | + '<img src="%s" style="height:%ipx" />' % (imname, maxheight) | ||
| 44 | + ] | ||
| 45 | + if not colors: | ||
| 46 | + colors = ['green']*len(bbs) | ||
| 47 | + html.extend([ bb(*box, ratio=ratio, color=color) for color,box in zip(colors,bbs) ]) | ||
| 48 | + html.append('</div>') | ||
| 49 | + return '\n'.join(html) | ||
| 50 | + | ||
| 51 | + | ||
| 52 | +def bb(x, y, width, height, ratio=1.0, color='green'): | ||
| 53 | + ''' | ||
| 54 | + Generates an HTML string bounding box. | ||
| 55 | + ''' | ||
| 56 | + html = '<div style="position:absolute; border:2px solid %s; color:%s; left:%ipx; top:%ipx; width:%ipx; height:%ipx;"></div>' | ||
| 57 | + return html % (color, color, x*ratio, y*ratio, width*ratio, height*ratio) |
share/openbr/cmake/InstallDependencies.cmake
| @@ -86,12 +86,14 @@ endfunction() | @@ -86,12 +86,14 @@ endfunction() | ||
| 86 | function(install_qt_platforms) | 86 | function(install_qt_platforms) |
| 87 | if(${BR_INSTALL_DEPENDENCIES}) | 87 | if(${BR_INSTALL_DEPENDENCIES}) |
| 88 | if(CMAKE_HOST_WIN32) | 88 | if(CMAKE_HOST_WIN32) |
| 89 | - #TODO | 89 | + install(FILES ${_qt5Core_install_prefix}/plugins/platforms/qwindows.dll |
| 90 | + DESTINATION bin/platforms) | ||
| 90 | elseif(CMAKE_HOST_APPLE) | 91 | elseif(CMAKE_HOST_APPLE) |
| 91 | install(FILES ${_qt5Core_install_prefix}/plugins/platforms/libqcocoa.dylib | 92 | install(FILES ${_qt5Core_install_prefix}/plugins/platforms/libqcocoa.dylib |
| 92 | DESTINATION bin/platforms) | 93 | DESTINATION bin/platforms) |
| 93 | else() | 94 | else() |
| 94 | - #TODO | 95 | + install(FILES ${_qt5Core_install_prefix}/plugins/platforms/libqlinuxfb.so |
| 96 | + DESTINATION bin/platforms) | ||
| 95 | endif() | 97 | endif() |
| 96 | endif() | 98 | endif() |
| 97 | endfunction() | 99 | endfunction() |
share/openbr/plotting/plot_utils.R
| @@ -217,6 +217,12 @@ formatData <- function(type="eval") { | @@ -217,6 +217,12 @@ formatData <- function(type="eval") { | ||
| 217 | NormLength <<- data[grep("NormLength",data$Plot),-c(1)] | 217 | NormLength <<- data[grep("NormLength",data$Plot),-c(1)] |
| 218 | sample <<- readImageData(Sample) | 218 | sample <<- readImageData(Sample) |
| 219 | rows <<- sample[[1]]$value | 219 | rows <<- sample[[1]]$value |
| 220 | + } else if (type == "knn") { | ||
| 221 | + # Split data into individual plots | ||
| 222 | + IET <<- data[grep("IET",data$Plot),-c(1)] | ||
| 223 | + IET$Y <<- as.numeric(as.character(IET$Y)) | ||
| 224 | + CMC <<- data[grep("CMC",data$Plot),-c(1)] | ||
| 225 | + CMC$Y <<- as.numeric(as.character(CMC$Y)) | ||
| 220 | } | 226 | } |
| 221 | } | 227 | } |
| 222 | 228 |