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 | 47 | int minwidth, |
| 48 | 48 | cv::CascadeClassifier cascade) // in: as percent of img width |
| 49 | 49 | { |
| 50 | - int leftborder = 0, topborder = 0; // border size in pixels | |
| 51 | - Image bordered_img(BORDER_FRAC == 0? | |
| 52 | - img: EnborderImg(leftborder, topborder, img)); | |
| 53 | - | |
| 54 | - // Detection results are very slightly better with equalization | |
| 55 | - // (tested on the MUCT images, which are not pre-equalized), and | |
| 56 | - // it's quick enough to equalize (roughly 10ms on a 1.6 GHz laptop). | |
| 57 | - | |
| 58 | - Image equalized_img; cv::equalizeHist(bordered_img, equalized_img); | |
| 59 | - | |
| 60 | 50 | CV_Assert(minwidth >= 1 && minwidth <= 100); |
| 61 | 51 | |
| 62 | - int minpix = MAX(100, cvRound(img.cols * minwidth / 100.)); | |
| 63 | - | |
| 64 | 52 | // the params below are accurate but slow |
| 65 | - static const double SCALE_FACTOR = 1.1; | |
| 66 | - static const int MIN_NEIGHBORS = 3; | |
| 53 | + static const double SCALE_FACTOR = 1.2; | |
| 54 | + static const int MIN_NEIGHBORS = 5; | |
| 67 | 55 | static const int DETECTOR_FLAGS = 0; |
| 68 | 56 | |
| 69 | 57 | vec_Rect facerects = // all face rects in image |
| 70 | - Detect(equalized_img, &cascade, NULL, | |
| 71 | - SCALE_FACTOR, MIN_NEIGHBORS, DETECTOR_FLAGS, minpix); | |
| 58 | + Detect(img, &cascade, NULL, | |
| 59 | + SCALE_FACTOR, MIN_NEIGHBORS, DETECTOR_FLAGS, 64); | |
| 72 | 60 | |
| 73 | 61 | // copy face rects into the detpars vector |
| 74 | 62 | |
| ... | ... | @@ -80,8 +68,6 @@ void DetectFaces( // all face rects into detpars |
| 80 | 68 | // detpar.x and detpar.y is the center of the face rectangle |
| 81 | 69 | detpar.x = facerect->x + facerect->width / 2.; |
| 82 | 70 | detpar.y = facerect->y + facerect->height / 2.; |
| 83 | - detpar.x -= leftborder; // discount the border we added earlier | |
| 84 | - detpar.y -= topborder; | |
| 85 | 71 | detpar.width = double(facerect->width); |
| 86 | 72 | detpar.height = double(facerect->height); |
| 87 | 73 | detpar.yaw = 0; // assume face has no yaw in this version of Stasm |
| ... | ... | @@ -187,10 +173,10 @@ void FaceDet::DetectFaces_( // call once per image to find all the faces |
| 187 | 173 | //CV_Assert(!facedet_g.empty()); // check that OpenFaceDetector_ was called |
| 188 | 174 | |
| 189 | 175 | DetectFaces(detpars_, img, minwidth, cascade); |
| 190 | - DiscardMissizedFaces(detpars_); | |
| 176 | + //DiscardMissizedFaces(detpars_); | |
| 191 | 177 | if (multiface) // order faces on increasing distance from left margin |
| 192 | 178 | { |
| 193 | - sort(detpars_.begin(), detpars_.end(), IncreasingLeftMargin); | |
| 179 | + sort(detpars_.begin(), detpars_.end(), DecreasingWidth); | |
| 194 | 180 | } |
| 195 | 181 | else |
| 196 | 182 | { | ... | ... |
3rdparty/stasm4.0.0/stasm/MOD_1/facedet.h
| ... | ... | @@ -38,13 +38,11 @@ public: |
| 38 | 38 | |
| 39 | 39 | FaceDet() {} // constructor |
| 40 | 40 | |
| 41 | - | |
| 42 | -private: | |
| 43 | - vector<DetPar> detpars_; // all the valid faces in the current image | |
| 44 | - | |
| 45 | 41 | int iface_; // index of current face for NextFace_ |
| 46 | 42 | // indexes into detpars_ |
| 43 | + vector<DetPar> detpars_; // all the valid faces in the current image | |
| 47 | 44 | |
| 45 | +private: | |
| 48 | 46 | DISALLOW_COPY_AND_ASSIGN(FaceDet); |
| 49 | 47 | |
| 50 | 48 | }; // end class FaceDet | ... | ... |
3rdparty/stasm4.0.0/stasm/stasm_lib.cpp
| ... | ... | @@ -160,7 +160,8 @@ int stasm_search_auto_ext( // extended version of stasm_search_auto |
| 160 | 160 | const char* data, |
| 161 | 161 | const int width, |
| 162 | 162 | const int height, |
| 163 | - StasmCascadeClassifier cascade) | |
| 163 | + StasmCascadeClassifier cascade, | |
| 164 | + FaceDet &detection) | |
| 164 | 165 | { |
| 165 | 166 | int returnval = 1; // assume success |
| 166 | 167 | *foundface = 0; // but assume no face found |
| ... | ... | @@ -176,15 +177,14 @@ int stasm_search_auto_ext( // extended version of stasm_search_auto |
| 176 | 177 | // Allocate image |
| 177 | 178 | Image img = Image(height, width,(unsigned char*)data); |
| 178 | 179 | |
| 179 | - FaceDet facedet; | |
| 180 | - | |
| 181 | 180 | // call the face detector to detect the face rectangle(s) |
| 182 | - facedet.DetectFaces_(img, NULL, false, 10, NULL, cascade.faceCascade); | |
| 181 | + if (detection.detpars_.empty()) | |
| 182 | + detection.DetectFaces_(img, NULL, true, 10, NULL, cascade.faceCascade); | |
| 183 | 183 | |
| 184 | 184 | // Get the start shape for the next face in the image, and the ROI around it. |
| 185 | 185 | // The shape will be wrt the ROI frame. |
| 186 | 186 | if (NextStartShapeAndRoi(shape, face_roi, detpar_roi, detpar, |
| 187 | - img, mods_g, facedet, cascade)) | |
| 187 | + img, mods_g, detection, cascade)) | |
| 188 | 188 | { |
| 189 | 189 | // now working with maybe flipped ROI and start shape in ROI frame |
| 190 | 190 | *foundface = 1; |
| ... | ... | @@ -219,9 +219,10 @@ int stasm_search_auto(// call repeatedly to find all faces |
| 219 | 219 | const char *data, |
| 220 | 220 | const int width, |
| 221 | 221 | const int height, |
| 222 | - StasmCascadeClassifier cascade) | |
| 222 | + StasmCascadeClassifier cascade, | |
| 223 | + FaceDet &detection) | |
| 223 | 224 | { |
| 224 | - return stasm_search_auto_ext(foundface, landmarks, NULL, data, width, height, cascade); | |
| 225 | + return stasm_search_auto_ext(foundface, landmarks, NULL, data, width, height, cascade, detection); | |
| 225 | 226 | } |
| 226 | 227 | |
| 227 | 228 | int stasm_search_single( // wrapper for stasm_search_auto and friends |
| ... | ... | @@ -237,7 +238,8 @@ int stasm_search_single( // wrapper for stasm_search_auto and friends |
| 237 | 238 | (void) datadir; |
| 238 | 239 | (void) imgpath; |
| 239 | 240 | |
| 240 | - return stasm_search_auto(foundface, landmarks, img, width, height, cascade); | |
| 241 | + FaceDet detection; | |
| 242 | + return stasm_search_auto(foundface, landmarks, img, width, height, cascade, detection); | |
| 241 | 243 | } |
| 242 | 244 | |
| 243 | 245 | int stasm_search_pinned( // call after the user has pinned some points | ... | ... |
3rdparty/stasm4.0.0/stasm/stasm_lib.h
| ... | ... | @@ -64,6 +64,10 @@ |
| 64 | 64 | |
| 65 | 65 | #include "stasmcascadeclassifier.h" |
| 66 | 66 | |
| 67 | +namespace stasm { | |
| 68 | + class FaceDet; | |
| 69 | +} | |
| 70 | + | |
| 67 | 71 | static const int stasm_NLANDMARKS = 77; // number of landmarks |
| 68 | 72 | |
| 69 | 73 | extern const char* const stasm_VERSION; |
| ... | ... | @@ -89,7 +93,8 @@ int stasm_search_auto( // call repeatedly to find all faces |
| 89 | 93 | const char* data, |
| 90 | 94 | const int width, |
| 91 | 95 | const int height, |
| 92 | - StasmCascadeClassifier cascade); | |
| 96 | + StasmCascadeClassifier cascade, | |
| 97 | + stasm::FaceDet &detection); | |
| 93 | 98 | |
| 94 | 99 | extern "C" |
| 95 | 100 | int stasm_search_single( // wrapper for stasm_search_auto and friends | ... | ... |
CMakeLists.txt
| ... | ... | @@ -13,7 +13,7 @@ if(NOT DEFINED CPACK_PACKAGE_VERSION_MAJOR) |
| 13 | 13 | set(CPACK_PACKAGE_VERSION_MAJOR 1) |
| 14 | 14 | endif() |
| 15 | 15 | if(NOT DEFINED CPACK_PACKAGE_VERSION_MINOR) |
| 16 | - set(CPACK_PACKAGE_VERSION_MINOR 0) | |
| 16 | + set(CPACK_PACKAGE_VERSION_MINOR 1) | |
| 17 | 17 | endif() |
| 18 | 18 | if(NOT DEFINED CPACK_PACKAGE_VERSION_PATCH) |
| 19 | 19 | set(CPACK_PACKAGE_VERSION_PATCH 0) |
| ... | ... | @@ -158,8 +158,8 @@ endif() |
| 158 | 158 | |
| 159 | 159 | # Download the models |
| 160 | 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 | 163 | SOURCE_DIR "${PROJECT_SOURCE_DIR}/share/openbr/models" |
| 164 | 164 | CONFIGURE_COMMAND "" |
| 165 | 165 | BUILD_COMMAND "" | ... | ... |
app/br/br.cpp
| ... | ... | @@ -169,8 +169,8 @@ public: |
| 169 | 169 | check(parc >= 2 && parc <= 4, "Incorrect parameter count for 'evalRegression'."); |
| 170 | 170 | br_eval_regression(parv[0], parv[1], parc >= 3 ? parv[2] : "", parc >= 4 ? parv[3] : ""); |
| 171 | 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 | 174 | } else if (!strcmp(fun, "pairwiseCompare")) { |
| 175 | 175 | check((parc >= 2) && (parc <= 3), "Incorrect parameter count for 'pairwiseCompare'."); |
| 176 | 176 | br_pairwise_compare(parv[0], parv[1], parc == 3 ? parv[2] : ""); |
| ... | ... | @@ -186,6 +186,9 @@ public: |
| 186 | 186 | } else if (!strcmp(fun, "plotMetadata")) { |
| 187 | 187 | check(parc >= 2, "Incorrect parameter count for 'plotMetadata'."); |
| 188 | 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 | 192 | } else if (!strcmp(fun, "project")) { |
| 190 | 193 | check(parc == 2, "Insufficient parameter count for 'project'."); |
| 191 | 194 | br_project(parv[0], parv[1]); |
| ... | ... | @@ -279,13 +282,14 @@ private: |
| 279 | 282 | "-evalDetection <predicted_gallery> <truth_gallery> [{csv}] [{normalize}] [{minSize}] [{maxSize}]\n" |
| 280 | 283 | "-evalLandmarking <predicted_gallery> <truth_gallery> [{csv} [<normalization_index_a> <normalization_index_b>] [sample_index] [total_examples]]\n" |
| 281 | 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 | 286 | "-pairwiseCompare <target_gallery> <query_gallery> [{output}]\n" |
| 284 | 287 | "-inplaceEval <simmat> <target> <query> [{csv}]\n" |
| 285 | 288 | "-assertEval <simmat> <mask> <accuracy>\n" |
| 286 | 289 | "-plotDetection <file> ... <file> {destination}\n" |
| 287 | 290 | "-plotLandmarking <file> ... <file> {destination}\n" |
| 288 | 291 | "-plotMetadata <file> ... <file> <columns>\n" |
| 292 | + "-plotKNN <file> ... <file> {destination}\n" | |
| 289 | 293 | "-project <input_gallery> {output_gallery}\n" |
| 290 | 294 | "-deduplicate <input_gallery> <output_gallery> <threshold>\n" |
| 291 | 295 | "-likely <input_type> <output_type> <output_likely_source>\n" | ... | ... |
docs/docs/api_docs/cl_api.md
| ... | ... | @@ -59,6 +59,17 @@ DOCUMENT ME |
| 59 | 59 | |
| 60 | 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 | 73 | ### -eval {: #eval } |
| 63 | 74 | |
| 64 | 75 | Evaluate a similarity matrix | ... | ... |
docs/docs/install.md
| ... | ... | @@ -35,7 +35,7 @@ A hacker's guide to building, editing, and running OpenBR. |
| 35 | 35 | |
| 36 | 36 | $ git clone https://github.com/biometrics/openbr.git |
| 37 | 37 | $ cd openbr |
| 38 | - $ git checkout 1.0 | |
| 38 | + $ git checkout v1.1.0 | |
| 39 | 39 | $ git submodule init |
| 40 | 40 | $ git submodule update |
| 41 | 41 | |
| ... | ... | @@ -116,7 +116,7 @@ A hacker's guide to building, editing, and running OpenBR. |
| 116 | 116 | |
| 117 | 117 | $ git clone https://github.com/biometrics/openbr.git |
| 118 | 118 | $ cd openbr |
| 119 | - $ git checkout 1.0 | |
| 119 | + $ git checkout v1.1.0 | |
| 120 | 120 | $ git submodule init |
| 121 | 121 | $ git submodule update |
| 122 | 122 | |
| ... | ... | @@ -196,7 +196,7 @@ A hacker's guide to building, editing, and running OpenBR. |
| 196 | 196 | $ cd /c |
| 197 | 197 | $ git clone https://github.com/biometrics/openbr.git |
| 198 | 198 | $ cd openbr |
| 199 | - $ git checkout 1.0 | |
| 199 | + $ git checkout v1.1.0 | |
| 200 | 200 | $ git submodule init |
| 201 | 201 | $ git submodule update |
| 202 | 202 | |
| ... | ... | @@ -277,7 +277,7 @@ A hacker's guide to building, editing, and running OpenBR. |
| 277 | 277 | |
| 278 | 278 | $ git clone https://github.com/biometrics/openbr.git |
| 279 | 279 | $ cd openbr |
| 280 | - $ git checkout 1.0 | |
| 280 | + $ git checkout v1.1.0 | |
| 281 | 281 | $ git submodule init |
| 282 | 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 | 131 | |
| 132 | 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 | 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 | 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 | 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 | 146 | //----------------------------- CascadeBoostParams ------------------------------------------------- | ... | ... |
openbr/core/boost.h
| ... | ... | @@ -2,7 +2,6 @@ |
| 2 | 2 | #define _BOOST_H_ |
| 3 | 3 | |
| 4 | 4 | #include "ml.h" |
| 5 | -#include <time.h> | |
| 6 | 5 | #include <openbr/openbr_plugin.h> |
| 7 | 6 | |
| 8 | 7 | #ifdef _WIN32 |
| ... | ... | @@ -17,9 +16,9 @@ namespace br |
| 17 | 16 | struct FeatureEvaluator |
| 18 | 17 | { |
| 19 | 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 | 23 | int getNumFeatures() const { return representation->numFeatures(); } |
| 25 | 24 | int getMaxCatCount() const { return representation->maxCatCount(); } |
| ... | ... | @@ -27,7 +26,8 @@ struct FeatureEvaluator |
| 27 | 26 | const cv::Mat& getCls() const { return cls; } |
| 28 | 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 | 31 | Representation *representation; |
| 32 | 32 | }; |
| 33 | 33 | ... | ... |
openbr/core/eval.cpp
| ... | ... | @@ -825,16 +825,25 @@ static QStringList computeDetectionResults(const QList<ResolvedDetection> &detec |
| 825 | 825 | if (prevFP / numImages < 0.1 && FP / numImages > 0.1 && discrete) { |
| 826 | 826 | qDebug("TAR @ FAR => %f : 0.1", TP / totalTrueDetections); |
| 827 | 827 | qDebug("Confidence: %f", detection.confidence); |
| 828 | + qDebug("TP vs. FP: %f to %f", TP, FP); | |
| 828 | 829 | } else if (prevFP / numImages < 0.01 && FP / numImages > 0.01 && discrete) { |
| 829 | 830 | qDebug("TAR @ FAR => %f : 0.01", TP / totalTrueDetections); |
| 830 | 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 | 839 | points.append(DetectionOperatingPoint(TP, FP, totalTrueDetections, numImages)); |
| 833 | 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 | 847 | const int keep = qMin(points.size(), Max_Points); |
| 839 | 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 | 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 | 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 | 1415 | if (numUnmatedSearches == 0) |
| 1407 | 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 | 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 | 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 | 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 | 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 | 1478 | } // namespace br | ... | ... |
openbr/core/eval.h
| ... | ... | @@ -33,7 +33,7 @@ namespace br |
| 33 | 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 | 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 | 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 | 38 | struct Candidate |
| 39 | 39 | { | ... | ... |
openbr/core/opencvutils.cpp
| ... | ... | @@ -436,7 +436,7 @@ public: |
| 436 | 436 | }; |
| 437 | 437 | |
| 438 | 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 | 441 | if (rects.isEmpty()) |
| 442 | 442 | return; |
| ... | ... | @@ -450,7 +450,7 @@ void OpenCVUtils::group(QList<Rect> &rects, QList<float> &confidences, float con |
| 450 | 450 | vector<Rect> rrects(nClasses); |
| 451 | 451 | |
| 452 | 452 | // Total number of rects in each class |
| 453 | - vector<int> rweights(nClasses, 0); | |
| 453 | + vector<int> neighbors(nClasses, 0); | |
| 454 | 454 | vector<float> rejectWeights(nClasses, -std::numeric_limits<float>::max()); |
| 455 | 455 | |
| 456 | 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 | 460 | rrects[cls].y += rects[i].y; |
| 461 | 461 | rrects[cls].width += rects[i].width; |
| 462 | 462 | rrects[cls].height += rects[i].height; |
| 463 | - rweights[cls]++; | |
| 463 | + neighbors[cls]++; | |
| 464 | 464 | } |
| 465 | 465 | |
| 466 | 466 | if (useConfidences) |
| ... | ... | @@ -478,7 +478,7 @@ void OpenCVUtils::group(QList<Rect> &rects, QList<float> &confidences, float con |
| 478 | 478 | for (int i = 0; i < nClasses; i++) |
| 479 | 479 | { |
| 480 | 480 | Rect r = rrects[i]; |
| 481 | - float s = 1.f/rweights[i]; | |
| 481 | + float s = 1.f/neighbors[i]; | |
| 482 | 482 | rrects[i] = Rect(saturate_cast<int>(r.x*s), |
| 483 | 483 | saturate_cast<int>(r.y*s), |
| 484 | 484 | saturate_cast<int>(r.width*s), |
| ... | ... | @@ -488,7 +488,7 @@ void OpenCVUtils::group(QList<Rect> &rects, QList<float> &confidences, float con |
| 488 | 488 | rects.clear(); |
| 489 | 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 | 492 | for (int i = 0; i < nClasses; i++) |
| 493 | 493 | { |
| 494 | 494 | // Average rectangle |
| ... | ... | @@ -496,19 +496,22 @@ void OpenCVUtils::group(QList<Rect> &rects, QList<float> &confidences, float con |
| 496 | 496 | |
| 497 | 497 | // Used to eliminate rectangles with few neighbors in the case of no weights |
| 498 | 498 | // int n1 = levelWeights ? rejectLevels[i] : rweights[i]; |
| 499 | - float w1 = rejectWeights[i]; | |
| 499 | + const float w1 = rejectWeights[i]; | |
| 500 | 500 | |
| 501 | 501 | // Eliminate rectangle if it doesn't meet confidence criteria |
| 502 | 502 | if (w1 <= confidenceThreshold) |
| 503 | 503 | continue; |
| 504 | 504 | |
| 505 | + const int n1 = neighbors[i]; | |
| 506 | + if (n1 < minNeighbors) | |
| 507 | + continue; | |
| 508 | + | |
| 505 | 509 | // filter out small face rectangles inside large rectangles |
| 506 | 510 | int j; |
| 507 | 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 | 515 | continue; |
| 513 | 516 | |
| 514 | 517 | Rect r2 = rrects[j]; |
| ... | ... | @@ -516,13 +519,11 @@ void OpenCVUtils::group(QList<Rect> &rects, QList<float> &confidences, float con |
| 516 | 519 | int dx = saturate_cast<int>(r2.width * epsilon); |
| 517 | 520 | int dy = saturate_cast<int>(r2.height * epsilon); |
| 518 | 521 | |
| 522 | + float w2 = rejectWeights[j]; | |
| 523 | + | |
| 519 | 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 | 527 | if(r1.x >= r2.x - dx && |
| 527 | 528 | r1.y >= r2.y - dy && |
| 528 | 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 | 532 | break; |
| 532 | 533 | } |
| 533 | 534 | |
| 534 | - // Need to return rects and confidences | |
| 535 | 535 | if( j == nClasses ) |
| 536 | 536 | { |
| 537 | 537 | rects.append(r1); | ... | ... |
openbr/core/opencvutils.h
| ... | ... | @@ -102,7 +102,7 @@ namespace OpenCVUtils |
| 102 | 102 | float overlap(const QRectF &rect1, const QRectF &rect2); |
| 103 | 103 | |
| 104 | 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 | 106 | void flip(const br::Template &src, br::Template &dst, int axis, bool flipMat=true, bool flipPoints=true, bool flipRects=true); |
| 107 | 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 | 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 | 377 | } // namespace br | ... | ... |
openbr/core/plot.h
| ... | ... | @@ -28,6 +28,7 @@ namespace br |
| 28 | 28 | bool PlotDetection(const QStringList &files, const File &destination, bool show = false); |
| 29 | 29 | bool PlotLandmarking(const QStringList &files, const File &destination, bool show = false); |
| 30 | 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 | 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 | 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 | 153 | void br_finalize() |
| ... | ... | @@ -221,6 +221,11 @@ bool br_plot_metadata(int num_files, const char *files[], const char *columns, b |
| 221 | 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 | 229 | float br_progress() |
| 225 | 230 | { |
| 226 | 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 | 64 | |
| 65 | 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 | 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 | 93 | |
| 94 | 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 | 98 | BR_EXPORT float br_progress(); |
| 97 | 99 | |
| 98 | 100 | BR_EXPORT void br_read_pipe(const char *pipe, int *argc, char ***argv); | ... | ... |
openbr/openbr_plugin.h
| ... | ... | @@ -882,13 +882,11 @@ public: |
| 882 | 882 | |
| 883 | 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 | 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 | 892 | virtual int numChannels() const { return 1; } |
| ... | ... | @@ -905,13 +903,13 @@ public: |
| 905 | 903 | |
| 906 | 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 | 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 | 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 | 125 | QList<Node*> classifiers; |
| 126 | 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 | 132 | CascadeBoostParams params(type, minTAR, maxFAR, trimRate, maxDepth, maxWeakCount); |
| 133 | 133 | |
| 134 | 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 | 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 | 143 | threshold = boost.getThreshold(); |
| 144 | 144 | |
| ... | ... | @@ -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 | 156 | float sum = 0; |
| 161 | 157 | for (int i = 0; i < classifiers.size(); i++) { |
| ... | ... | @@ -163,10 +159,10 @@ private: |
| 163 | 159 | |
| 164 | 160 | while (node->left) { |
| 165 | 161 | if (representation->maxCatCount() > 0) { |
| 166 | - int c = (int)representation->evaluate(m, node->featureIdx); | |
| 162 | + int c = (int)representation->evaluate(t, node->featureIdx); | |
| 167 | 163 | node = (node->subset[c >> 5] & (1 << (c & 31))) ? node->left : node->right; |
| 168 | 164 | } else { |
| 169 | - double val = representation->evaluate(m, node->featureIdx); | |
| 165 | + double val = representation->evaluate(t, node->featureIdx); | |
| 170 | 166 | node = val <= node->threshold ? node->left : node->right; |
| 171 | 167 | } |
| 172 | 168 | } |
| ... | ... | @@ -184,11 +180,9 @@ private: |
| 184 | 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 | 188 | Size windowSize(int *dx, int *dy) const | ... | ... |
openbr/plugins/classification/cascade.cpp
| 1 | 1 | #include <opencv2/imgproc/imgproc.hpp> |
| 2 | -#include <opencv2/highgui/highgui.hpp> | |
| 2 | + | |
| 3 | 3 | #include <openbr/plugins/openbr_internal.h> |
| 4 | 4 | #include <openbr/core/common.h> |
| 5 | +#include "openbr/core/opencvutils.h" | |
| 5 | 6 | |
| 6 | 7 | #include <QtConcurrent> |
| 7 | 8 | |
| ... | ... | @@ -37,7 +38,7 @@ struct Miner |
| 37 | 38 | Mat mine(bool *newImg) |
| 38 | 39 | { |
| 39 | 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 | 42 | (void*)(scaledSrc.data + point.y * scaledSrc.step + point.x * scaledSrc.elemSize()), |
| 42 | 43 | scaledSrc.step); |
| 43 | 44 | |
| ... | ... | @@ -100,31 +101,30 @@ class CascadeClassifier : public Classifier |
| 100 | 101 | BR_PROPERTY(bool, requireAllStages, false) |
| 101 | 102 | |
| 102 | 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 | 107 | QList<int> indices; |
| 107 | 108 | int negIndex, posIndex, samplingRound; |
| 108 | 109 | |
| 109 | - QMutex samplingMutex, miningMutex, passedMutex; | |
| 110 | + QMutex samplingMutex, miningMutex; | |
| 110 | 111 | |
| 111 | 112 | void init() |
| 112 | 113 | { |
| 113 | 114 | negIndex = posIndex = samplingRound = 0; |
| 114 | 115 | } |
| 115 | 116 | |
| 116 | - bool getPositive(Mat &img) | |
| 117 | + bool getPositive(Template &img) | |
| 117 | 118 | { |
| 118 | 119 | if (posIndex >= posImages.size()) |
| 119 | 120 | return false; |
| 120 | - | |
| 121 | - posImages[indices[posIndex++]].copyTo(img); | |
| 121 | + img = posImages[indices[posIndex++]]; | |
| 122 | 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 | 129 | const Size size = windowSize(); |
| 130 | 130 | // Grab negative from list |
| ... | ... | @@ -136,9 +136,9 @@ class CascadeClassifier : public Classifier |
| 136 | 136 | samplingRound = samplingRound % (size.width * size.height); |
| 137 | 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 | 142 | && offset.x >= 0 && offset.y >= 0) |
| 143 | 143 | break; |
| 144 | 144 | } |
| ... | ... | @@ -146,35 +146,32 @@ class CascadeClassifier : public Classifier |
| 146 | 146 | return negative; |
| 147 | 147 | } |
| 148 | 148 | |
| 149 | - int mine() | |
| 149 | + uint64 mine() | |
| 150 | 150 | { |
| 151 | - int passedNegatives = 0; | |
| 151 | + uint64 passedNegatives = 0; | |
| 152 | 152 | forever { |
| 153 | - Mat negative; | |
| 153 | + Template negative; | |
| 154 | 154 | Point offset; |
| 155 | - samplingMutex.lock(); | |
| 155 | + QMutexLocker samplingLocker(&samplingMutex); | |
| 156 | 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 | 160 | forever { |
| 161 | 161 | bool newImg; |
| 162 | - Mat sample = miner.mine(&newImg); | |
| 162 | + Template sample(negative.file, miner.mine(&newImg)); | |
| 163 | 163 | if (!newImg) { |
| 164 | 164 | if (negSamples.size() >= numNegs) |
| 165 | 165 | return passedNegatives; |
| 166 | 166 | |
| 167 | 167 | float confidence; |
| 168 | 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 | 171 | return passedNegatives; |
| 173 | - } | |
| 174 | 172 | |
| 175 | 173 | negSamples.append(sample); |
| 176 | 174 | printf("Negative samples: %d\r", negSamples.size()); |
| 177 | - miningMutex.unlock(); | |
| 178 | 175 | } |
| 179 | 176 | |
| 180 | 177 | passedNegatives++; |
| ... | ... | @@ -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 | 190 | << "\nTotal positive images:" << posImages.size() |
| 194 | 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 | 195 | stages.reserve(numStages); |
| 199 | 196 | for (int i = 0; i < numStages; i++) { |
| ... | ... | @@ -212,27 +209,17 @@ class CascadeClassifier : public Classifier |
| 212 | 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 | 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 | 220 | float stageConf = 0.0f; |
| 234 | 221 | foreach (const Classifier *stage, stages) { |
| 235 | - float result = stage->classify(image, process, &stageConf); | |
| 222 | + float result = stage->classify(src, process, &stageConf); | |
| 236 | 223 | if (confidence) |
| 237 | 224 | *confidence += stageConf; |
| 238 | 225 | if (result == 0.0f) |
| ... | ... | @@ -246,9 +233,9 @@ class CascadeClassifier : public Classifier |
| 246 | 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 | 241 | Size windowSize(int *dx = NULL, int *dy = NULL) const |
| ... | ... | @@ -276,16 +263,13 @@ class CascadeClassifier : public Classifier |
| 276 | 263 | private: |
| 277 | 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 | 268 | posIndex = 0; |
| 284 | 269 | |
| 285 | 270 | float confidence; |
| 286 | 271 | while (posSamples.size() < numPos) { |
| 287 | - Mat pos(windowSize(), CV_8UC1); | |
| 288 | - | |
| 272 | + Template pos; | |
| 289 | 273 | if (!getPositive(pos)) |
| 290 | 274 | qFatal("Cannot get another positive sample!"); |
| 291 | 275 | |
| ... | ... | @@ -297,13 +281,13 @@ private: |
| 297 | 281 | |
| 298 | 282 | qDebug() << "POS count : consumed " << posSamples.size() << ":" << posIndex; |
| 299 | 283 | |
| 300 | - QFutureSynchronizer<int> futures; | |
| 284 | + QFutureSynchronizer<uint64> futures; | |
| 301 | 285 | for (int i=0; i<QThread::idealThreadCount(); i++) |
| 302 | 286 | futures.addFuture(QtConcurrent::run(this, &CascadeClassifier::mine)); |
| 303 | 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 | 291 | for (int i=0; i<results.size(); i++) |
| 308 | 292 | passedNegs += results[i].result(); |
| 309 | 293 | ... | ... |
openbr/plugins/classification/dlib.cpp
| 1 | +#include <opencv2/imgproc/imgproc.hpp> | |
| 1 | 2 | #include <dlib/image_processing/frontal_face_detector.h> |
| 2 | 3 | #include <dlib/opencv.h> |
| 3 | 4 | |
| ... | ... | @@ -45,14 +46,18 @@ private: |
| 45 | 46 | { |
| 46 | 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 | 56 | array2d<unsigned char> image; |
| 52 | 57 | assign_image(image,cimg); |
| 53 | 58 | |
| 54 | 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 | 61 | full_object_detection shape = (*sp)(image, r); |
| 57 | 62 | for (size_t i = 0; i < shape.num_parts(); i++) |
| 58 | 63 | dst.file.appendPoint(QPointF(shape.part(i)(0),shape.part(i)(1))); | ... | ... |
openbr/plugins/classification/liblinear.cpp
openbr/plugins/core/algorithms.cpp
| ... | ... | @@ -39,7 +39,7 @@ class AlgorithmsInitializer : public Initializer |
| 39 | 39 | Globals->abbreviations.insert("FR_Mouth", "(CropFromLandmarks([59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76])+Resize(24,48))"); |
| 40 | 40 | Globals->abbreviations.insert("FR_Nose", "(CropFromLandmarks([16,17,18,19,20,21,22,23,24,25,26,27],padding=3)+Resize(36,36))"); |
| 41 | 41 | Globals->abbreviations.insert("FR_Face", "(Crop(24,24,88,88)+Resize(44,44))"); |
| 42 | - Globals->abbreviations.insert("FR_Detect", "(FaceDetection+Stasm+Rename(StasmLeftEye,Affine_1,true)+Rename(StasmRightEye,Affine_0,true)+Affine(136,136,0.35,0.35,warpPoints=true))"); | |
| 42 | + Globals->abbreviations.insert("FR_Detect", "(Open+Cvt(Gray)+Cascade+Stasm+Rename(StasmLeftEye,Affine_1,true)+Rename(StasmRightEye,Affine_0,true)+Affine(136,136,0.35,0.35,warpPoints=true))"); | |
| 43 | 43 | Globals->abbreviations.insert("FR_Represent", "((DenseHOG/DenseLBP)+Cat+LDA(.98)+Normalize(L2))"); |
| 44 | 44 | |
| 45 | 45 | Globals->abbreviations.insert("GenderClassification", "FaceDetection+Expand+FaceClassificationRegistration+Expand+<FaceClassificationExtraction>+<GenderClassifier>+Discard"); | ... | ... |
openbr/plugins/distance/unit.cpp
| ... | ... | @@ -76,12 +76,18 @@ class UnitDistance : public Distance |
| 76 | 76 | |
| 77 | 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 | 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 | 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 | 35 | using namespace cv; |
| 30 | 36 | |
| 31 | 37 | namespace br | ... | ... |
openbr/plugins/imgproc/equalizehist.cpp
| ... | ... | @@ -18,6 +18,8 @@ |
| 18 | 18 | |
| 19 | 19 | #include <openbr/plugins/openbr_internal.h> |
| 20 | 20 | |
| 21 | +using namespace cv; | |
| 22 | + | |
| 21 | 23 | namespace br |
| 22 | 24 | { |
| 23 | 25 | |
| ... | ... | @@ -32,7 +34,20 @@ class EqualizeHistTransform : public UntrainableTransform |
| 32 | 34 | |
| 33 | 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 | 2 | #include <openbr/plugins/openbr_internal.h> |
| 2 | 3 | |
| 3 | 4 | using namespace cv; |
| ... | ... | @@ -5,24 +6,36 @@ using namespace cv; |
| 5 | 6 | namespace br |
| 6 | 7 | { |
| 7 | 8 | |
| 9 | +/*! | |
| 10 | + * \ingroup transforms | |
| 11 | + * \brief Pads an image. | |
| 12 | + * \author Scott Klum \cite sklum | |
| 13 | + */ | |
| 8 | 14 | class PadTransform : public UntrainableTransform |
| 9 | 15 | { |
| 10 | 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 | 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 | 55 | |
| 56 | 56 | void project(const Template &src, Template &dst) const |
| 57 | 57 | { |
| 58 | - if (!preserveAspect) | |
| 58 | + if (!preserveAspect) { | |
| 59 | 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 | 67 | float inRatio = (float) src.m().rows / src.m().cols; |
| 62 | 68 | float outRatio = (float) rows / columns; |
| 63 | 69 | dst = Mat::zeros(rows, columns, src.m().type()); | ... | ... |
openbr/plugins/imgproc/rndrotate.cpp
| ... | ... | @@ -16,6 +16,7 @@ |
| 16 | 16 | |
| 17 | 17 | #include <opencv2/imgproc/imgproc.hpp> |
| 18 | 18 | #include <openbr/plugins/openbr_internal.h> |
| 19 | +#include <openbr/core/opencvutils.h> | |
| 19 | 20 | |
| 20 | 21 | using namespace cv; |
| 21 | 22 | |
| ... | ... | @@ -32,12 +33,14 @@ class RndRotateTransform : public UntrainableTransform |
| 32 | 33 | Q_OBJECT |
| 33 | 34 | |
| 34 | 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 | 37 | BR_PROPERTY(QList<int>, range, QList<int>() << -15 << 15) |
| 38 | + BR_PROPERTY(int, center, -1) | |
| 36 | 39 | |
| 37 | 40 | void project(const Template &src, Template &dst) const { |
| 38 | 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 | 44 | warpAffine(src,dst,rotMatrix,Size(src.m().cols,src.m().rows),INTER_LINEAR,BORDER_REFLECT_101); |
| 42 | 45 | |
| 43 | 46 | QList<QPointF> points = src.file.points(); | ... | ... |
openbr/plugins/imgproc/roi.cpp
| ... | ... | @@ -34,8 +34,10 @@ class ROITransform : public UntrainableTransform |
| 34 | 34 | Q_OBJECT |
| 35 | 35 | Q_PROPERTY(QString propName READ get_propName WRITE set_propName RESET reset_propName STORED false) |
| 36 | 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 | 38 | BR_PROPERTY(QString, propName, "") |
| 38 | 39 | BR_PROPERTY(bool, copyOnCrop, false) |
| 40 | + BR_PROPERTY(int, shiftPoints, -1) | |
| 39 | 41 | |
| 40 | 42 | void project(const Template &src, Template &dst) const |
| 41 | 43 | { |
| ... | ... | @@ -55,6 +57,16 @@ class ROITransform : public UntrainableTransform |
| 55 | 57 | if (Globals->verbose) |
| 56 | 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 | 70 | dst.file.clearRects(); |
| 59 | 71 | |
| 60 | 72 | if (copyOnCrop) | ... | ... |
openbr/plugins/imgproc/slidingwindow.cpp
| ... | ... | @@ -17,7 +17,6 @@ |
| 17 | 17 | #include <openbr/plugins/openbr_internal.h> |
| 18 | 18 | #include <openbr/core/opencvutils.h> |
| 19 | 19 | #include <openbr/core/qtutils.h> |
| 20 | -#include <opencv2/highgui/highgui.hpp> | |
| 21 | 20 | |
| 22 | 21 | #include <opencv2/imgproc/imgproc.hpp> |
| 23 | 22 | |
| ... | ... | @@ -35,7 +34,6 @@ namespace br |
| 35 | 34 | * \br_property int minSize The smallest sized object to detect in pixels |
| 36 | 35 | * \br_property int maxSize The largest sized object to detect in pixels. A negative value will set maxSize == image size |
| 37 | 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 | 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 | 38 | * \br_property float eps Parameter for non-maximum supression |
| 41 | 39 | */ |
| ... | ... | @@ -50,6 +48,7 @@ class SlidingWindowTransform : public MetaTransform |
| 50 | 48 | Q_PROPERTY(float scaleFactor READ get_scaleFactor WRITE set_scaleFactor RESET reset_scaleFactor STORED false) |
| 51 | 49 | Q_PROPERTY(float confidenceThreshold READ get_confidenceThreshold WRITE set_confidenceThreshold RESET reset_confidenceThreshold STORED false) |
| 52 | 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 | 53 | BR_PROPERTY(br::Classifier*, classifier, NULL) |
| 55 | 54 | BR_PROPERTY(int, minSize, 20) |
| ... | ... | @@ -57,10 +56,11 @@ class SlidingWindowTransform : public MetaTransform |
| 57 | 56 | BR_PROPERTY(float, scaleFactor, 1.2) |
| 58 | 57 | BR_PROPERTY(float, confidenceThreshold, 10) |
| 59 | 58 | BR_PROPERTY(float, eps, 0.2) |
| 59 | + BR_PROPERTY(int, minNeighbors, 3) | |
| 60 | 60 | |
| 61 | 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 | 66 | void project(const Template &src, Template &dst) const |
| ... | ... | @@ -123,15 +123,18 @@ class SlidingWindowTransform : public MetaTransform |
| 123 | 123 | |
| 124 | 124 | Mat scaledImage(scaledImageSize, CV_8U, imageBuffer.data); |
| 125 | 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 | 130 | int step = factor > 2. ? 1 : 2; |
| 129 | 131 | for (int y = 0; y < processingRectSize.height; y += step) { |
| 130 | 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 | 136 | float confidence = 0; |
| 134 | - int result = classifier->classify(window, false, &confidence); | |
| 137 | + int result = classifier->classify(t, false, &confidence); | |
| 135 | 138 | |
| 136 | 139 | if (result == 1) { |
| 137 | 140 | rects.append(Rect(cvRound(x*factor), cvRound(y*factor), windowSize.width, windowSize.height)); |
| ... | ... | @@ -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 | 154 | if (!enrollAll && rects.empty()) { |
| 152 | 155 | rects.append(Rect(0, 0, m.cols, m.rows)); | ... | ... |
openbr/plugins/metadata/averagepoints.cpp
| ... | ... | @@ -14,23 +14,14 @@ class AveragePointsTransform : public UntrainableMetadataTransform |
| 14 | 14 | Q_PROPERTY(QList<int> indices READ get_indices WRITE set_indices RESET reset_indices STORED false) |
| 15 | 15 | Q_PROPERTY(QString metaName READ get_metaName WRITE set_metaName RESET reset_metaName STORED true) |
| 16 | 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 | 17 | BR_PROPERTY(QList<int>, indices, QList<int>()) |
| 19 | 18 | BR_PROPERTY(QString, metaName, "") |
| 20 | 19 | BR_PROPERTY(bool, append, false) |
| 21 | - BR_PROPERTY(int, nLandmarks, 51) | |
| 22 | 20 | |
| 23 | 21 | void projectMetadata(const File &src, File &dst) const |
| 24 | 22 | { |
| 25 | 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 | 26 | for (int i = 0; i < indices.size(); i++) { |
| 36 | 27 | x1 += src.points()[indices[i]].x(); | ... | ... |
openbr/plugins/metadata/cascade.cpp
| ... | ... | @@ -202,7 +202,8 @@ class CascadeTransform : public MetaTransform |
| 202 | 202 | Q_PROPERTY(int minSize READ get_minSize WRITE set_minSize RESET reset_minSize STORED false) |
| 203 | 203 | Q_PROPERTY(int minNeighbors READ get_minNeighbors WRITE set_minNeighbors RESET reset_minNeighbors STORED false) |
| 204 | 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 | 207 | // Training parameters |
| 207 | 208 | Q_PROPERTY(int numStages READ get_numStages WRITE set_numStages RESET reset_numStages STORED false) |
| 208 | 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 | 228 | BR_PROPERTY(int, minSize, 64) |
| 228 | 229 | BR_PROPERTY(int, minNeighbors, 5) |
| 229 | 230 | BR_PROPERTY(bool, ROCMode, false) |
| 230 | - | |
| 231 | + BR_PROPERTY(float, scaleFactor, 1.2) | |
| 232 | + | |
| 231 | 233 | // Training parameters - Default values provided trigger OpenCV defaults |
| 232 | 234 | BR_PROPERTY(int, numStages, -1) |
| 233 | 235 | BR_PROPERTY(int, w, -1) |
| ... | ... | @@ -410,8 +412,19 @@ class CascadeTransform : public MetaTransform |
| 410 | 412 | std::vector<Rect> rects; |
| 411 | 413 | std::vector<int> rejectLevels; |
| 412 | 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 | 429 | bool empty = false; |
| 417 | 430 | if (!enrollAll && rects.empty()) { |
| ... | ... | @@ -426,7 +439,7 @@ class CascadeTransform : public MetaTransform |
| 426 | 439 | u.file.set("Confidence",-std::numeric_limits<float>::max()); |
| 427 | 440 | } else if (rejectLevels.size() > j) |
| 428 | 441 | u.file.set("Confidence", rejectLevels[j]*levelWeights[j]); |
| 429 | - else | |
| 442 | + else | |
| 430 | 443 | u.file.set("Confidence", rects[j].area()); |
| 431 | 444 | const QRectF rect = OpenCVUtils::fromRect(rects[j]); |
| 432 | 445 | u.file.appendRect(rect); | ... | ... |
openbr/plugins/metadata/keytorect.cpp renamed to openbr/plugins/metadata/keytolandmark.cpp
| ... | ... | @@ -24,28 +24,35 @@ namespace br |
| 24 | 24 | * \brief Convert values of key_X, key_Y, key_Width, key_Height to a rect. |
| 25 | 25 | * \author Jordan Cheney \cite JordanCheney |
| 26 | 26 | */ |
| 27 | -class KeyToRectTransform : public UntrainableMetadataTransform | |
| 27 | +class KeyToLandmarkTransform : public UntrainableMetadataTransform | |
| 28 | 28 | { |
| 29 | 29 | Q_OBJECT |
| 30 | 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 | 32 | BR_PROPERTY(QString, key, "") |
| 33 | + BR_PROPERTY(bool, point, false) | |
| 32 | 34 | |
| 33 | 35 | void projectMetadata(const File &src, File &dst) const |
| 34 | 36 | { |
| 35 | 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 | 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 | 31 | Q_OBJECT |
| 32 | 32 | Q_PROPERTY(QString regexp READ get_regexp WRITE set_regexp RESET reset_regexp STORED false) |
| 33 | 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 | 35 | BR_PROPERTY(QString, regexp, "") |
| 35 | 36 | BR_PROPERTY(QString, key, "") |
| 37 | + BR_PROPERTY(bool, keep, false) | |
| 36 | 38 | |
| 37 | 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 | 1 | #include <QString> |
| 2 | -#include <stasm_lib.h> | |
| 3 | 2 | #include <stasmcascadeclassifier.h> |
| 3 | +#include <stasm_lib.h> | |
| 4 | +#include <stasm.h> | |
| 4 | 5 | #include <opencv2/opencv.hpp> |
| 5 | 6 | |
| 6 | 7 | #include <openbr/plugins/openbr_internal.h> |
| ... | ... | @@ -56,18 +57,18 @@ BR_REGISTER(Initializer, StasmInitializer) |
| 56 | 57 | * \brief Wraps STASM key point detector |
| 57 | 58 | * \author Scott Klum \cite sklum |
| 58 | 59 | */ |
| 59 | -class StasmTransform : public UntrainableTransform | |
| 60 | +class StasmTransform : public UntrainableMetaTransform | |
| 60 | 61 | { |
| 61 | 62 | Q_OBJECT |
| 62 | 63 | |
| 63 | 64 | Q_PROPERTY(bool stasm3Format READ get_stasm3Format WRITE set_stasm3Format RESET reset_stasm3Format STORED false) |
| 64 | 65 | BR_PROPERTY(bool, stasm3Format, false) |
| 65 | - Q_PROPERTY(bool clearLandmarks READ get_clearLandmarks WRITE set_clearLandmarks RESET reset_clearLandmarks STORED false) | |
| 66 | - BR_PROPERTY(bool, clearLandmarks, false) | |
| 67 | 66 | Q_PROPERTY(QList<float> pinPoints READ get_pinPoints WRITE set_pinPoints RESET reset_pinPoints STORED false) |
| 68 | 67 | BR_PROPERTY(QList<float>, pinPoints, QList<float>()) |
| 69 | 68 | Q_PROPERTY(QStringList pinLabels READ get_pinLabels WRITE set_pinLabels RESET reset_pinLabels STORED false) |
| 70 | 69 | BR_PROPERTY(QStringList, pinLabels, QStringList()) |
| 70 | + Q_PROPERTY(QString inputVariable READ get_inputVariable WRITE set_inputVariable RESET reset_inputVariable STORED false) | |
| 71 | + BR_PROPERTY(QString, inputVariable, "FrontalFace") | |
| 71 | 72 | |
| 72 | 73 | Resource<StasmCascadeClassifier> stasmCascadeResource; |
| 73 | 74 | |
| ... | ... | @@ -77,86 +78,119 @@ class StasmTransform : public UntrainableTransform |
| 77 | 78 | stasmCascadeResource.setResourceMaker(new StasmResourceMaker()); |
| 78 | 79 | } |
| 79 | 80 | |
| 80 | - void project(const Template &src, Template &dst) const | |
| 81 | + QList<QPointF> convertLandmarks(int nLandmarks, float *landmarks) const | |
| 81 | 82 | { |
| 82 | - Mat stasmSrc(src); | |
| 83 | - if (src.m().channels() == 3) | |
| 84 | - cvtColor(src, stasmSrc, CV_BGR2GRAY); | |
| 85 | - else if (src.m().channels() != 1) | |
| 86 | - qFatal("Stasm expects single channel matrices."); | |
| 87 | - | |
| 88 | - if (!stasmSrc.isContinuous()) | |
| 89 | - qFatal("Stasm expects continuous matrix data."); | |
| 90 | - dst = src; | |
| 91 | - | |
| 92 | - int foundFace = 0; | |
| 93 | - int nLandmarks = stasm_NLANDMARKS; | |
| 94 | - float landmarks[2 * stasm_NLANDMARKS]; | |
| 95 | - | |
| 96 | - bool searchPinned = false; | |
| 97 | - | |
| 98 | - QPointF rightEye, leftEye; | |
| 99 | - /* Two use cases are accounted for: | |
| 100 | - * 1. Pin eyes without normalization: in this case the string list should contain the KEYS for right then left eyes, respectively. | |
| 101 | - * 2. Pin eyes with normalization: in this case the string list should contain the COORDINATES of the right then left eyes, respectively. | |
| 102 | - * Currently, we only support normalization with a transformation such that the src file contains Affine_0 and Affine_1. Checking for | |
| 103 | - * these keys prevents us from pinning eyes on a face that wasn't actually transformed (see AffineTransform). | |
| 104 | - * If both cases fail, we default to stasm_search_single. */ | |
| 105 | - | |
| 106 | - if (!pinPoints.isEmpty() && src.file.contains("Affine_0") && src.file.contains("Affine_1")) { | |
| 107 | - rightEye = QPointF(pinPoints.at(0), pinPoints.at(1)); | |
| 108 | - leftEye = QPointF(pinPoints.at(2), pinPoints.at(3)); | |
| 109 | - searchPinned = true; | |
| 110 | - } else if (!pinLabels.isEmpty()) { | |
| 111 | - rightEye = src.file.get<QPointF>(pinLabels.at(0), QPointF()); | |
| 112 | - leftEye = src.file.get<QPointF>(pinLabels.at(1), QPointF()); | |
| 113 | - searchPinned = true; | |
| 114 | - } | |
| 115 | - | |
| 116 | - if (searchPinned) { | |
| 117 | - float pins[2 * stasm_NLANDMARKS]; | |
| 118 | - | |
| 119 | - for (int i = 0; i < nLandmarks; i++) { | |
| 120 | - if (i == 38) /* Stasm Right Eye */ { pins[2*i] = rightEye.x(); pins[2*i+1] = rightEye.y(); } | |
| 121 | - else if (i == 39) /* Stasm Left Eye */ { pins[2*i] = leftEye.x(); pins[2*i+1] = leftEye.y(); } | |
| 122 | - else { pins[2*i] = 0; pins[2*i+1] = 0; } | |
| 123 | - } | |
| 83 | + if (stasm3Format) | |
| 84 | + stasm_convert_shape(landmarks, 76); | |
| 124 | 85 | |
| 125 | - stasm_search_pinned(landmarks, pins, reinterpret_cast<const char*>(stasmSrc.data), stasmSrc.cols, stasmSrc.rows, NULL); | |
| 126 | - | |
| 127 | - // The ASM in Stasm is guaranteed to converge in this case | |
| 128 | - foundFace = 1; | |
| 86 | + QList<QPointF> points; | |
| 87 | + for (int i = 0; i < nLandmarks; i++) { | |
| 88 | + QPointF point(landmarks[2 * i], landmarks[2 * i + 1]); | |
| 89 | + points.append(point); | |
| 129 | 90 | } |
| 130 | 91 | |
| 131 | - if (!foundFace) { | |
| 132 | - StasmCascadeClassifier *stasmCascade = stasmCascadeResource.acquire(); | |
| 133 | - stasm_search_single(&foundFace, landmarks, reinterpret_cast<const char*>(stasmSrc.data), stasmSrc.cols, stasmSrc.rows, *stasmCascade, NULL, NULL); | |
| 134 | - stasmCascadeResource.release(stasmCascade); | |
| 135 | - } | |
| 92 | + return points; | |
| 93 | + } | |
| 136 | 94 | |
| 137 | - if (stasm3Format) { | |
| 138 | - nLandmarks = 76; | |
| 139 | - stasm_convert_shape(landmarks, nLandmarks); | |
| 140 | - } | |
| 95 | + void project(const Template &src, Template &dst) const | |
| 96 | + { | |
| 97 | + TemplateList temp; | |
| 98 | + project(TemplateList() << src, temp); | |
| 99 | + if (!temp.isEmpty()) dst = temp.first(); | |
| 100 | + } | |
| 141 | 101 | |
| 142 | - // For convenience, if these are the only points/rects we want to deal with as the algorithm progresses | |
| 143 | - if (clearLandmarks) { | |
| 144 | - dst.file.clearPoints(); | |
| 145 | - dst.file.clearRects(); | |
| 146 | - } | |
| 102 | + void project(const TemplateList &src, TemplateList &dst) const | |
| 103 | + { | |
| 104 | + foreach (const Template &t, src) { | |
| 105 | + Mat stasmSrc(t); | |
| 106 | + if (t.m().channels() == 3) | |
| 107 | + cvtColor(t, stasmSrc, CV_BGR2GRAY); | |
| 108 | + else if (t.m().channels() != 1) | |
| 109 | + qFatal("Stasm expects single channel matrices."); | |
| 110 | + | |
| 111 | + if (!stasmSrc.isContinuous()) | |
| 112 | + qFatal("Stasm expects continuous matrix data."); | |
| 113 | + | |
| 114 | + int foundFace = 0; | |
| 115 | + int nLandmarks = stasm_NLANDMARKS; | |
| 116 | + float landmarks[2 * stasm_NLANDMARKS]; | |
| 117 | + | |
| 118 | + bool searchPinned = false; | |
| 119 | + | |
| 120 | + QPointF rightEye, leftEye; | |
| 121 | + /* Two use cases are accounted for: | |
| 122 | + * 1. Pin eyes without normalization: in this case the string list should contain the KEYS for right then left eyes, respectively. | |
| 123 | + * 2. Pin eyes with normalization: in this case the string list should contain the COORDINATES of the right then left eyes, respectively. | |
| 124 | + * Currently, we only support normalization with a transformation such that the src file contains Affine_0 and Affine_1. Checking for | |
| 125 | + * these keys prevents us from pinning eyes on a face that wasn't actually transformed (see AffineTransform). | |
| 126 | + * If both cases fail, we default to stasm_search_single. */ | |
| 127 | + | |
| 128 | + if (!pinPoints.isEmpty() && t.file.contains("Affine_0") && t.file.contains("Affine_1")) { | |
| 129 | + rightEye = QPointF(pinPoints.at(0), pinPoints.at(1)); | |
| 130 | + leftEye = QPointF(pinPoints.at(2), pinPoints.at(3)); | |
| 131 | + searchPinned = true; | |
| 132 | + } else if (!pinLabels.isEmpty()) { | |
| 133 | + rightEye = t.file.get<QPointF>(pinLabels.at(0), QPointF()); | |
| 134 | + leftEye = t.file.get<QPointF>(pinLabels.at(1), QPointF()); | |
| 135 | + searchPinned = true; | |
| 136 | + } | |
| 137 | + | |
| 138 | + if (searchPinned) { | |
| 139 | + float pins[2 * stasm_NLANDMARKS]; | |
| 140 | + | |
| 141 | + for (int i = 0; i < nLandmarks; i++) { | |
| 142 | + if (i == 38) /* Stasm Right Eye */ { pins[2*i] = rightEye.x(); pins[2*i+1] = rightEye.y(); } | |
| 143 | + else if (i == 39) /* Stasm Left Eye */ { pins[2*i] = leftEye.x(); pins[2*i+1] = leftEye.y(); } | |
| 144 | + else { pins[2*i] = 0; pins[2*i+1] = 0; } | |
| 145 | + } | |
| 146 | + | |
| 147 | + stasm_search_pinned(landmarks, pins, reinterpret_cast<const char*>(stasmSrc.data), stasmSrc.cols, stasmSrc.rows, NULL); | |
| 148 | + | |
| 149 | + // The ASM in Stasm is guaranteed to converge in this case | |
| 150 | + foundFace = 1; | |
| 151 | + Template u(t.file, t.m()); | |
| 152 | + QList<QPointF> points = convertLandmarks(nLandmarks, landmarks); | |
| 153 | + u.file.set("StasmRightEye", points[38]); | |
| 154 | + u.file.set("StasmLeftEye", points[39]); | |
| 155 | + u.file.appendPoints(points); | |
| 156 | + dst.append(u); | |
| 157 | + } | |
| 147 | 158 | |
| 148 | - if (!foundFace) { | |
| 149 | - if (Globals->verbose) qWarning("No face found in %s.", qPrintable(src.file.fileName())); | |
| 150 | - dst.file.fte = true; | |
| 151 | - } else { | |
| 152 | - QList<QPointF> points; | |
| 153 | - for (int i = 0; i < nLandmarks; i++) { | |
| 154 | - QPointF point(landmarks[2 * i], landmarks[2 * i + 1]); | |
| 155 | - points.append(point); | |
| 159 | + if (!foundFace) { | |
| 160 | + StasmCascadeClassifier *stasmCascade = stasmCascadeResource.acquire(); | |
| 161 | + foundFace = 1; | |
| 162 | + stasm::FaceDet detection; | |
| 163 | + | |
| 164 | + QList<QRectF> rects = t.file.contains(inputVariable) ? QList<QRectF>() << t.file.get<QRectF>(inputVariable) : t.file.rects(); | |
| 165 | + if (!rects.isEmpty()) detection.iface_ = 0; | |
| 166 | + for (int i=0; i<rects.size(); i++) { | |
| 167 | + Rect rect = OpenCVUtils::toRect(rects[i]); | |
| 168 | + stasm::DetPar detpar; | |
| 169 | + detpar.x = rect.x + rect.width / 2.; | |
| 170 | + detpar.y = rect.y + rect.height / 2.; | |
| 171 | + detpar.width = double(rect.width); | |
| 172 | + detpar.height = double(rect.height); | |
| 173 | + detpar.yaw = 0; | |
| 174 | + detpar.eyaw = stasm::EYAW00; | |
| 175 | + detection.detpars_.push_back(detpar); | |
| 176 | + } | |
| 177 | + | |
| 178 | + while (foundFace) { | |
| 179 | + stasm_search_auto(&foundFace, landmarks, reinterpret_cast<const char*>(stasmSrc.data), stasmSrc.cols, stasmSrc.rows, *stasmCascade, detection); | |
| 180 | + if (foundFace) { | |
| 181 | + Template u(t.file, t.m()); | |
| 182 | + QList<QPointF> points = convertLandmarks(nLandmarks, landmarks); | |
| 183 | + u.file.set("StasmRightEye", points[38]); | |
| 184 | + u.file.set("StasmLeftEye", points[39]); | |
| 185 | + u.file.appendPoints(points); | |
| 186 | + dst.append(u); | |
| 187 | + } | |
| 188 | + | |
| 189 | + if (!Globals->enrollAll) | |
| 190 | + break; | |
| 191 | + } | |
| 192 | + stasmCascadeResource.release(stasmCascade); | |
| 156 | 193 | } |
| 157 | - dst.file.set("StasmRightEye", points[38]); | |
| 158 | - dst.file.set("StasmLeftEye", points[39]); | |
| 159 | - dst.file.appendPoints(points); | |
| 160 | 194 | } |
| 161 | 195 | } |
| 162 | 196 | }; | ... | ... |
openbr/plugins/output/knn.cpp
| ... | ... | @@ -6,44 +6,80 @@ |
| 6 | 6 | namespace br |
| 7 | 7 | { |
| 8 | 8 | |
| 9 | +typedef QPair<float,int> Pair; | |
| 10 | + | |
| 9 | 11 | /*! |
| 10 | 12 | * \ingroup outputs |
| 11 | 13 | * \brief Outputs the k-Nearest Neighbors from the gallery for each probe. |
| 12 | 14 | * \author Ben Klein \cite bhklein |
| 13 | 15 | */ |
| 14 | -class knnOutput : public MatrixOutput | |
| 16 | +class knnOutput : public Output | |
| 15 | 17 | { |
| 16 | 18 | Q_OBJECT |
| 17 | 19 | |
| 20 | + int rowBlock, columnBlock; | |
| 21 | + size_t headerSize, k; | |
| 22 | + cv::Mat blockScores; | |
| 23 | + | |
| 18 | 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 | 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 | 75 | Candidate candidate((size_t)pair.second, pair.first); |
| 41 | 76 | neighbors.push_back(candidate); |
| 42 | 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 | 83 | f.close(); |
| 48 | 84 | } |
| 49 | 85 | }; | ... | ... |
openbr/plugins/representation/haar.cpp
| ... | ... | @@ -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 | 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 | 97 | int size = indices.empty() ? numFeatures() : indices.size(); |
| 96 | 98 | |
| 97 | 99 | Mat result(1, size, CV_32FC1); |
| 98 | 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 | 102 | return result; |
| 101 | 103 | } |
| 102 | 104 | ... | ... |
openbr/plugins/representation/mblbp.cpp
| ... | ... | @@ -39,32 +39,36 @@ class MBLBPRepresentation : public Representation |
| 39 | 39 | |
| 40 | 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 | 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 | 67 | int size = indices.empty() ? numFeatures() : indices.size(); |
| 64 | 68 | |
| 65 | 69 | Mat result(1, size, CV_32FC1); |
| 66 | 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 | 72 | return result; |
| 69 | 73 | } |
| 70 | 74 | ... | ... |
openbr/plugins/representation/npd.cpp
| ... | ... | @@ -16,23 +16,25 @@ class NPDRepresentation : public Representation |
| 16 | 16 | |
| 17 | 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 | 33 | int size = indices.empty() ? numFeatures() : indices.size(); |
| 32 | 34 | |
| 33 | 35 | Mat result(1, size, CV_32FC1); |
| 34 | 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 | 38 | return result; |
| 37 | 39 | } |
| 38 | 40 | |
| ... | ... | @@ -49,10 +51,10 @@ class NPDRepresentation : public Representation |
| 49 | 51 | struct Feature |
| 50 | 52 | { |
| 51 | 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 | 55 | float calc(const Mat &image) const; |
| 54 | 56 | |
| 55 | - int p[2]; | |
| 57 | + int p0, p1; | |
| 56 | 58 | }; |
| 57 | 59 | QList<Feature> features; |
| 58 | 60 | }; |
| ... | ... | @@ -61,9 +63,9 @@ BR_REGISTER(Representation, NPDRepresentation) |
| 61 | 63 | |
| 62 | 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 | 71 | } // namespace br | ... | ... |
openbr/plugins/representation/random.cpp
| ... | ... | @@ -28,9 +28,9 @@ class RandomRepresentation : public Representation |
| 28 | 28 | |
| 29 | 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 | 35 | const int nFeatures = representation->numFeatures(); |
| 36 | 36 | |
| ... | ... | @@ -40,17 +40,17 @@ class RandomRepresentation : public Representation |
| 40 | 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 | 55 | QList<int> newIndices; |
| 56 | 56 | if (indices.empty()) |
| ... | ... | @@ -59,7 +59,7 @@ class RandomRepresentation : public Representation |
| 59 | 59 | for (int i = 0; i < indices.size(); i++) |
| 60 | 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 | 65 | int numFeatures() const |
| ... | ... | @@ -108,6 +108,3 @@ BR_REGISTER(Representation, RandomRepresentation) |
| 108 | 108 | } // namespace br |
| 109 | 109 | |
| 110 | 110 | #include "representation/random.moc" |
| 111 | - | |
| 112 | - | |
| 113 | - | ... | ... |
scripts/face_cluster_viz.py renamed to scripts/brpy/face_cluster_viz.py
| 1 | 1 | #!/usr/bin/env python |
| 2 | 2 | |
| 3 | -from PIL import Image | |
| 4 | 3 | import csv, sys, json, argparse |
| 4 | +from brpy.html_viz import crop_to_bb | |
| 5 | 5 | |
| 6 | 6 | parser = argparse.ArgumentParser(description='Visualize face cluster results in an HTML page.') |
| 7 | 7 | parser.add_argument('input_file', type=str, help='Results from clustering (in csv format)') |
| ... | ... | @@ -16,24 +16,15 @@ clustmap = dict() |
| 16 | 16 | with open(args.input_file) as f: |
| 17 | 17 | for line in csv.DictReader(f): |
| 18 | 18 | c = int(line[args.cluster_key]) |
| 19 | + if c not in clustmap: | |
| 20 | + clustmap[c] = [] | |
| 19 | 21 | x,y,width,height = [ float(line[k]) for k in ('Face_X','Face_Y','Face_Width','Face_Height') ] |
| 20 | 22 | imname = '%s/%s' % (args.img_loc, line['File']) |
| 21 | 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 | 25 | except IOError: |
| 25 | 26 | print('problem with %s' % imname) |
| 26 | 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 | 28 | clustmap[c].append(html) |
| 38 | 29 | |
| 39 | 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 | 86 | function(install_qt_platforms) |
| 87 | 87 | if(${BR_INSTALL_DEPENDENCIES}) |
| 88 | 88 | if(CMAKE_HOST_WIN32) |
| 89 | - #TODO | |
| 89 | + install(FILES ${_qt5Core_install_prefix}/plugins/platforms/qwindows.dll | |
| 90 | + DESTINATION bin/platforms) | |
| 90 | 91 | elseif(CMAKE_HOST_APPLE) |
| 91 | 92 | install(FILES ${_qt5Core_install_prefix}/plugins/platforms/libqcocoa.dylib |
| 92 | 93 | DESTINATION bin/platforms) |
| 93 | 94 | else() |
| 94 | - #TODO | |
| 95 | + install(FILES ${_qt5Core_install_prefix}/plugins/platforms/libqlinuxfb.so | |
| 96 | + DESTINATION bin/platforms) | |
| 95 | 97 | endif() |
| 96 | 98 | endif() |
| 97 | 99 | endfunction() | ... | ... |
share/openbr/plotting/plot_utils.R
| ... | ... | @@ -217,6 +217,12 @@ formatData <- function(type="eval") { |
| 217 | 217 | NormLength <<- data[grep("NormLength",data$Plot),-c(1)] |
| 218 | 218 | sample <<- readImageData(Sample) |
| 219 | 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 | ... | ... |