Commit 8748d391e60b3c50556cbe2f5f45b5c6473a657d
Merge branch 'master' of https://github.com/biometrics/openbr
Showing
39 changed files
with
1806 additions
and
448 deletions
app/br/CMakeLists.txt
| ... | ... | @@ -4,6 +4,8 @@ endif() |
| 4 | 4 | |
| 5 | 5 | add_executable(br br.cpp ${BR_RESOURCES}) |
| 6 | 6 | target_link_libraries(br openbr ${CMAKE_THREAD_LIBS_INIT}) |
| 7 | +qt5_use_modules(br ${QT_DEPENDENCIES}) | |
| 8 | + | |
| 7 | 9 | install(TARGETS br RUNTIME DESTINATION bin) |
| 8 | 10 | |
| 9 | 11 | add_test(NAME br_initialize WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND br) | ... | ... |
app/br/br.cpp
| ... | ... | @@ -14,6 +14,9 @@ |
| 14 | 14 | * limitations under the License. * |
| 15 | 15 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ |
| 16 | 16 | |
| 17 | +#include <QCoreApplication> | |
| 18 | +#include <QRunnable> | |
| 19 | +#include <QThreadPool> | |
| 17 | 20 | #include <stdio.h> |
| 18 | 21 | #include <stdlib.h> |
| 19 | 22 | #include <string.h> |
| ... | ... | @@ -47,171 +50,193 @@ |
| 47 | 50 | * \endcode |
| 48 | 51 | */ |
| 49 | 52 | |
| 50 | -static void help() | |
| 53 | +class FakeMain : public QRunnable | |
| 51 | 54 | { |
| 52 | - printf("<arg> = Input; {arg} = Output; [arg] = Optional; (arg0|...|argN) = Choice\n" | |
| 53 | - "\n" | |
| 54 | - "==== Core Commands ====\n" | |
| 55 | - "-train <gallery> ... <gallery> [{model}]\n" | |
| 56 | - "-enroll <input_gallery> ... <input_gallery> {output_gallery}\n" | |
| 57 | - "-compare <target_gallery> <query_gallery> [{output}]\n" | |
| 58 | - "-eval <simmat> <mask> [{csv}]\n" | |
| 59 | - "-plot <file> ... <file> {destination}\n" | |
| 60 | - "\n" | |
| 61 | - "==== Other Commands ====\n" | |
| 62 | - "-fuse <simmat> ... <simmat> <mask> (None|MinMax|ZScore|WScore) (Min|Max|Sum[W1:W2:...:Wn]|Replace|Difference|None) {simmat}\n" | |
| 63 | - "-cluster <simmat> ... <simmat> <aggressiveness> {csv}\n" | |
| 64 | - "-makeMask <target_gallery> <query_gallery> {mask}\n" | |
| 65 | - "-combineMasks <mask> ... <mask> {mask} (And|Or)\n" | |
| 66 | - "-cat <gallery> ... <gallery> {gallery}\n" | |
| 67 | - "-convert <template> {template}\n" | |
| 68 | - "-reformat <target_sigset> <query_sigset> <simmat> {output}\n" | |
| 69 | - "-evalClassification <predicted_gallery> <truth_gallery>\n" | |
| 70 | - "-evalRegression <predicted_gallery> <truth_gallery>\n" | |
| 71 | - "-evalClusters <clusters> <sigset>\n" | |
| 72 | - "-confusion <file> <score>\n" | |
| 73 | - "-plotMetadata <file> ... <file> <columns>\n" | |
| 74 | - "\n" | |
| 75 | - "==== Configuration ====\n" | |
| 76 | - "-<key> <value>\n" | |
| 77 | - "\n" | |
| 78 | - "==== Miscellaneous ====\n" | |
| 79 | - "-objects [abstraction [implementation]]\n" | |
| 80 | - "-about\n" | |
| 81 | - "-version\n" | |
| 82 | - "-shell\n" | |
| 83 | - "-exit\n"); | |
| 84 | -} | |
| 55 | + int argc; | |
| 56 | + char **argv; | |
| 85 | 57 | |
| 86 | -static void check(bool condition, const char *error_message) | |
| 87 | -{ | |
| 88 | - if (!condition) { | |
| 89 | - printf("%s\n", error_message); | |
| 90 | - br_finalize(); | |
| 91 | - exit(1); | |
| 92 | - } | |
| 93 | -} | |
| 58 | +public: | |
| 59 | + FakeMain(int argc_, char **argv_) | |
| 60 | + : argc(argc_), argv(argv_) {} | |
| 94 | 61 | |
| 95 | -int main(int argc, char *argv[]) | |
| 96 | -{ | |
| 97 | - br_initialize(argc, argv); | |
| 62 | + void run() | |
| 63 | + { | |
| 64 | + // Remove program name | |
| 65 | + argv = &argv[1]; | |
| 66 | + argc--; | |
| 98 | 67 | |
| 99 | - // Remove program name | |
| 100 | - argv = &argv[1]; | |
| 101 | - argc--; | |
| 102 | - | |
| 103 | - if (argc == 0) printf("%s\nTry running 'br -help'\n", br_about()); | |
| 104 | - | |
| 105 | - bool shell = false; | |
| 106 | - while (shell || (argc > 0)) { | |
| 107 | - const char *fun; | |
| 108 | - int parc; | |
| 109 | - const char **parv; | |
| 110 | - if (shell) { | |
| 111 | - printf("> "); | |
| 112 | - br_read_line(&parc, &parv); | |
| 113 | - if (parc == 0) continue; | |
| 114 | - fun = parv[0]; | |
| 115 | - parc--; | |
| 116 | - parv = &parv[1]; | |
| 117 | - } else /* argc > 0 */ { | |
| 118 | - fun = &argv[0][1]; | |
| 119 | - parc = 0; while ((1+parc < argc) && (argv[1+parc][0] != '-')) parc++; | |
| 120 | - parv = (const char **)&argv[1]; | |
| 121 | - argc = argc - (1+parc); | |
| 122 | - argv = &argv[parc+1]; | |
| 123 | - } | |
| 68 | + if (argc == 0) printf("%s\nTry running 'br -help'\n", br_about()); | |
| 124 | 69 | |
| 125 | - // Core Tasks | |
| 126 | - if (!strcmp(fun, "train")) { | |
| 127 | - check(parc >= 1, "Insufficient parameter count for 'train'."); | |
| 128 | - br_train_n(parc == 1 ? 1 : parc-1, parv, parc == 1 ? "" : parv[parc-1]); | |
| 129 | - } else if (!strcmp(fun, "enroll")) { | |
| 130 | - check(parc >= 1, "Insufficient parameter count for 'enroll'."); | |
| 131 | - if (parc == 1) br_enroll(parv[0]); | |
| 132 | - else br_enroll_n(parc-1, parv, parv[parc-1]); | |
| 133 | - } else if (!strcmp(fun, "compare")) { | |
| 134 | - check((parc >= 2) && (parc <= 3), "Incorrect parameter count for 'compare'."); | |
| 135 | - br_compare(parv[0], parv[1], parc == 3 ? parv[2] : ""); | |
| 136 | - } else if (!strcmp(fun, "eval")) { | |
| 137 | - check((parc >= 2) && (parc <= 3), "Incorrect parameter count for 'eval'."); | |
| 138 | - br_eval(parv[0], parv[1], parc == 3 ? parv[2] : ""); | |
| 139 | - } else if (!strcmp(fun, "plot")) { | |
| 140 | - check(parc >= 2, "Incorrect parameter count for 'plot'."); | |
| 141 | - br_plot(parc-1, parv, parv[parc-1], true); | |
| 142 | - } | |
| 70 | + bool shell = false; | |
| 71 | + while (shell || (argc > 0)) { | |
| 72 | + const char *fun; | |
| 73 | + int parc; | |
| 74 | + const char **parv; | |
| 75 | + if (shell) { | |
| 76 | + printf("> "); | |
| 77 | + br_read_line(&parc, &parv); | |
| 78 | + if (parc == 0) continue; | |
| 79 | + fun = parv[0]; | |
| 80 | + parc--; | |
| 81 | + parv = &parv[1]; | |
| 82 | + } else /* argc > 0 */ { | |
| 83 | + fun = &argv[0][1]; | |
| 84 | + parc = 0; while ((1+parc < argc) && (argv[1+parc][0] != '-')) parc++; | |
| 85 | + parv = (const char **)&argv[1]; | |
| 86 | + argc = argc - (1+parc); | |
| 87 | + argv = &argv[parc+1]; | |
| 88 | + } | |
| 89 | + | |
| 90 | + // Core Tasks | |
| 91 | + if (!strcmp(fun, "train")) { | |
| 92 | + check(parc >= 1, "Insufficient parameter count for 'train'."); | |
| 93 | + br_train_n(parc == 1 ? 1 : parc-1, parv, parc == 1 ? "" : parv[parc-1]); | |
| 94 | + } else if (!strcmp(fun, "enroll")) { | |
| 95 | + check(parc >= 1, "Insufficient parameter count for 'enroll'."); | |
| 96 | + if (parc == 1) br_enroll(parv[0]); | |
| 97 | + else br_enroll_n(parc-1, parv, parv[parc-1]); | |
| 98 | + } else if (!strcmp(fun, "compare")) { | |
| 99 | + check((parc >= 2) && (parc <= 3), "Incorrect parameter count for 'compare'."); | |
| 100 | + br_compare(parv[0], parv[1], parc == 3 ? parv[2] : ""); | |
| 101 | + } else if (!strcmp(fun, "eval")) { | |
| 102 | + check((parc >= 2) && (parc <= 3), "Incorrect parameter count for 'eval'."); | |
| 103 | + br_eval(parv[0], parv[1], parc == 3 ? parv[2] : ""); | |
| 104 | + } else if (!strcmp(fun, "plot")) { | |
| 105 | + check(parc >= 2, "Incorrect parameter count for 'plot'."); | |
| 106 | + br_plot(parc-1, parv, parv[parc-1], true); | |
| 107 | + } | |
| 143 | 108 | |
| 144 | - // Secondary Tasks | |
| 145 | - else if (!strcmp(fun, "fuse")) { | |
| 146 | - check(parc >= 5, "Insufficient parameter count for 'fuse'."); | |
| 147 | - br_fuse(parc-4, parv, parv[parc-4], parv[parc-3], parv[parc-2], parv[parc-1]); | |
| 148 | - } else if (!strcmp(fun, "cluster")) { | |
| 149 | - check(parc >= 3, "Insufficient parameter count for 'cluster'."); | |
| 150 | - br_cluster(parc-2, parv, atof(parv[parc-2]), parv[parc-1]); | |
| 151 | - } else if (!strcmp(fun, "makeMask")) { | |
| 152 | - check(parc == 3, "Incorrect parameter count for 'makeMask'."); | |
| 153 | - br_make_mask(parv[0], parv[1], parv[2]); | |
| 154 | - } else if (!strcmp(fun, "combineMasks")) { | |
| 155 | - check(parc >= 4, "Insufficient parameter count for 'combineMasks'."); | |
| 156 | - br_combine_masks(parc-2, parv, parv[parc-2], parv[parc-1]); | |
| 157 | - } else if (!strcmp(fun, "cat")) { | |
| 158 | - check(parc >= 2, "Insufficient parameter count for 'cat'."); | |
| 159 | - br_cat(parc-1, parv, parv[parc-1]); | |
| 160 | - } else if (!strcmp(fun, "convert")) { | |
| 161 | - check(parc == 2, "Incorrect parameter count for 'convert'."); | |
| 162 | - br_convert(parv[0], parv[1]); | |
| 163 | - } else if (!strcmp(fun, "reformat")) { | |
| 164 | - check(parc == 4, "Incorrect parameter count for 'reformat'."); | |
| 165 | - br_reformat(parv[0], parv[1], parv[2], parv[3]); | |
| 166 | - } else if (!strcmp(fun, "evalClassification")) { | |
| 167 | - check(parc == 2, "Incorrect parameter count for 'evalClassification'."); | |
| 168 | - br_eval_classification(parv[0], parv[1]); | |
| 169 | - } else if (!strcmp(fun, "evalRegression")) { | |
| 170 | - check(parc == 2, "Incorrect parameter count for 'evalRegression'."); | |
| 171 | - br_eval_regression(parv[0], parv[1]); | |
| 172 | - } else if (!strcmp(fun, "evalClusters")) { | |
| 173 | - check(parc == 2, "Incorrect parameter count for 'evalClusters'."); | |
| 174 | - br_eval_clustering(parv[0], parv[1]); | |
| 175 | - } else if (!strcmp(fun, "confusion")) { | |
| 176 | - check(parc == 2, "Incorrect parameter count for 'confusion'."); | |
| 177 | - int true_positives, false_positives, true_negatives, false_negatives; | |
| 178 | - br_confusion(parv[0], atof(parv[1]), | |
| 179 | - &true_positives, &false_positives, &true_negatives, &false_negatives); | |
| 180 | - printf("True Positives = %d\nFalse Positives = %d\nTrue Negatives = %d\nFalseNegatives = %d\n", | |
| 181 | - true_positives, false_positives, true_negatives, false_negatives); | |
| 182 | - } else if (!strcmp(fun, "plotMetadata")) { | |
| 183 | - check(parc >= 2, "Incorrect parameter count for 'plotMetadata'."); | |
| 184 | - br_plot_metadata(parc-1, parv, parv[parc-1], true); | |
| 109 | + // Secondary Tasks | |
| 110 | + else if (!strcmp(fun, "fuse")) { | |
| 111 | + check(parc >= 5, "Insufficient parameter count for 'fuse'."); | |
| 112 | + br_fuse(parc-4, parv, parv[parc-4], parv[parc-3], parv[parc-2], parv[parc-1]); | |
| 113 | + } else if (!strcmp(fun, "cluster")) { | |
| 114 | + check(parc >= 3, "Insufficient parameter count for 'cluster'."); | |
| 115 | + br_cluster(parc-2, parv, atof(parv[parc-2]), parv[parc-1]); | |
| 116 | + } else if (!strcmp(fun, "makeMask")) { | |
| 117 | + check(parc == 3, "Incorrect parameter count for 'makeMask'."); | |
| 118 | + br_make_mask(parv[0], parv[1], parv[2]); | |
| 119 | + } else if (!strcmp(fun, "combineMasks")) { | |
| 120 | + check(parc >= 4, "Insufficient parameter count for 'combineMasks'."); | |
| 121 | + br_combine_masks(parc-2, parv, parv[parc-2], parv[parc-1]); | |
| 122 | + } else if (!strcmp(fun, "cat")) { | |
| 123 | + check(parc >= 2, "Insufficient parameter count for 'cat'."); | |
| 124 | + br_cat(parc-1, parv, parv[parc-1]); | |
| 125 | + } else if (!strcmp(fun, "convert")) { | |
| 126 | + check(parc == 2, "Incorrect parameter count for 'convert'."); | |
| 127 | + br_convert(parv[0], parv[1]); | |
| 128 | + } else if (!strcmp(fun, "reformat")) { | |
| 129 | + check(parc == 4, "Incorrect parameter count for 'reformat'."); | |
| 130 | + br_reformat(parv[0], parv[1], parv[2], parv[3]); | |
| 131 | + } else if (!strcmp(fun, "evalClassification")) { | |
| 132 | + check(parc == 2, "Incorrect parameter count for 'evalClassification'."); | |
| 133 | + br_eval_classification(parv[0], parv[1]); | |
| 134 | + } else if (!strcmp(fun, "evalRegression")) { | |
| 135 | + check(parc == 2, "Incorrect parameter count for 'evalRegression'."); | |
| 136 | + br_eval_regression(parv[0], parv[1]); | |
| 137 | + } else if (!strcmp(fun, "evalClusters")) { | |
| 138 | + check(parc == 2, "Incorrect parameter count for 'evalClusters'."); | |
| 139 | + br_eval_clustering(parv[0], parv[1]); | |
| 140 | + } else if (!strcmp(fun, "confusion")) { | |
| 141 | + check(parc == 2, "Incorrect parameter count for 'confusion'."); | |
| 142 | + int true_positives, false_positives, true_negatives, false_negatives; | |
| 143 | + br_confusion(parv[0], atof(parv[1]), | |
| 144 | + &true_positives, &false_positives, &true_negatives, &false_negatives); | |
| 145 | + printf("True Positives = %d\nFalse Positives = %d\nTrue Negatives = %d\nFalseNegatives = %d\n", | |
| 146 | + true_positives, false_positives, true_negatives, false_negatives); | |
| 147 | + } else if (!strcmp(fun, "plotMetadata")) { | |
| 148 | + check(parc >= 2, "Incorrect parameter count for 'plotMetadata'."); | |
| 149 | + br_plot_metadata(parc-1, parv, parv[parc-1], true); | |
| 150 | + } | |
| 151 | + | |
| 152 | + // Miscellaneous | |
| 153 | + else if (!strcmp(fun, "help")) { | |
| 154 | + check(parc == 0, "No parameters expected for 'help'."); | |
| 155 | + help(); | |
| 156 | + } else if (!strcmp(fun, "objects")) { | |
| 157 | + check(parc <= 2, "Incorrect parameter count for 'objects'."); | |
| 158 | + printf("%s\n", br_objects(parc >= 1 ? parv[0] : ".*", parc >= 2 ? parv[1] : ".*")); | |
| 159 | + } else if (!strcmp(fun, "about")) { | |
| 160 | + check(parc == 0, "No parameters expected for 'about'."); | |
| 161 | + printf("%s\n", br_about()); | |
| 162 | + } else if (!strcmp(fun, "version")) { | |
| 163 | + check(parc == 0, "No parameters expected for 'version'."); | |
| 164 | + printf("%s\n", br_version()); | |
| 165 | + } else if (!strcmp(fun, "shell")) { | |
| 166 | + check(parc == 0, "No parameters expected for 'shell'."); | |
| 167 | + shell = true; | |
| 168 | + } else if (!strcmp(fun, "exit")) { | |
| 169 | + check(parc == 0, "No parameters expected for 'exit'."); | |
| 170 | + shell = false; | |
| 171 | + } else if (!strcmp(fun, "br")) { | |
| 172 | + printf("That's me!\n"); | |
| 173 | + } else if (parc <= 1) { | |
| 174 | + br_set_property(fun, parc >=1 ? parv[0] : ""); | |
| 175 | + } else { | |
| 176 | + printf("Unrecognized function '%s'\n", fun); | |
| 177 | + } | |
| 185 | 178 | } |
| 186 | 179 | |
| 187 | - // Miscellaneous | |
| 188 | - else if (!strcmp(fun, "help")) { | |
| 189 | - check(parc == 0, "No parameters expected for 'help'."); | |
| 190 | - help(); | |
| 191 | - } else if (!strcmp(fun, "objects")) { | |
| 192 | - check(parc <= 2, "Incorrect parameter count for 'objects'."); | |
| 193 | - printf("%s\n", br_objects(parc >= 1 ? parv[0] : ".*", parc >= 2 ? parv[1] : ".*")); | |
| 194 | - } else if (!strcmp(fun, "about")) { | |
| 195 | - check(parc == 0, "No parameters expected for 'about'."); | |
| 196 | - printf("%s\n", br_about()); | |
| 197 | - } else if (!strcmp(fun, "version")) { | |
| 198 | - check(parc == 0, "No parameters expected for 'version'."); | |
| 199 | - printf("%s\n", br_version()); | |
| 200 | - } else if (!strcmp(fun, "shell")) { | |
| 201 | - check(parc == 0, "No parameters expected for 'shell'."); | |
| 202 | - shell = true; | |
| 203 | - } else if (!strcmp(fun, "exit")) { | |
| 204 | - check(parc == 0, "No parameters expected for 'exit'."); | |
| 205 | - shell = false; | |
| 206 | - } else if (!strcmp(fun, "br")) { | |
| 207 | - printf("That's me!\n"); | |
| 208 | - } else if (parc <= 1) { | |
| 209 | - br_set_property(fun, parc >=1 ? parv[0] : ""); | |
| 210 | - } else { | |
| 211 | - printf("Unrecognized function '%s'\n", fun); | |
| 180 | + QCoreApplication::exit(); | |
| 181 | + } | |
| 182 | + | |
| 183 | +private: | |
| 184 | + static void check(bool condition, const char *error_message) | |
| 185 | + { | |
| 186 | + if (!condition) { | |
| 187 | + printf("%s\n", error_message); | |
| 188 | + QCoreApplication::exit(); | |
| 212 | 189 | } |
| 213 | 190 | } |
| 214 | 191 | |
| 192 | + static void help() | |
| 193 | + { | |
| 194 | + printf("<arg> = Input; {arg} = Output; [arg] = Optional; (arg0|...|argN) = Choice\n" | |
| 195 | + "\n" | |
| 196 | + "==== Core Commands ====\n" | |
| 197 | + "-train <gallery> ... <gallery> [{model}]\n" | |
| 198 | + "-enroll <input_gallery> ... <input_gallery> {output_gallery}\n" | |
| 199 | + "-compare <target_gallery> <query_gallery> [{output}]\n" | |
| 200 | + "-eval <simmat> <mask> [{csv}]\n" | |
| 201 | + "-plot <file> ... <file> {destination}\n" | |
| 202 | + "\n" | |
| 203 | + "==== Other Commands ====\n" | |
| 204 | + "-fuse <simmat> ... <simmat> <mask> (None|MinMax|ZScore|WScore) (Min|Max|Sum[W1:W2:...:Wn]|Replace|Difference|None) {simmat}\n" | |
| 205 | + "-cluster <simmat> ... <simmat> <aggressiveness> {csv}\n" | |
| 206 | + "-makeMask <target_gallery> <query_gallery> {mask}\n" | |
| 207 | + "-combineMasks <mask> ... <mask> {mask} (And|Or)\n" | |
| 208 | + "-cat <gallery> ... <gallery> {gallery}\n" | |
| 209 | + "-convert <template> {template}\n" | |
| 210 | + "-reformat <target_sigset> <query_sigset> <simmat> {output}\n" | |
| 211 | + "-evalClassification <predicted_gallery> <truth_gallery>\n" | |
| 212 | + "-evalRegression <predicted_gallery> <truth_gallery>\n" | |
| 213 | + "-evalClusters <clusters> <sigset>\n" | |
| 214 | + "-confusion <file> <score>\n" | |
| 215 | + "-plotMetadata <file> ... <file> <columns>\n" | |
| 216 | + "\n" | |
| 217 | + "==== Configuration ====\n" | |
| 218 | + "-<key> <value>\n" | |
| 219 | + "\n" | |
| 220 | + "==== Miscellaneous ====\n" | |
| 221 | + "-objects [abstraction [implementation]]\n" | |
| 222 | + "-about\n" | |
| 223 | + "-version\n" | |
| 224 | + "-shell\n" | |
| 225 | + "-exit\n"); | |
| 226 | + } | |
| 227 | +}; | |
| 228 | + | |
| 229 | +int main(int argc, char *argv[]) | |
| 230 | +{ | |
| 231 | + br_initialize(argc, argv); | |
| 232 | + | |
| 233 | + // Do argument execution in another thread so this main thread can run an event loop. | |
| 234 | + // When adding fakeMain to the global thread pool we give it a higher (slower) priority | |
| 235 | + // so that it is preempted when parallel work is available, | |
| 236 | + // otherwise we would only achieve (n-1)/n CPU utilization. | |
| 237 | + FakeMain *fakeMain = new FakeMain(argc, argv); | |
| 238 | + QThreadPool::globalInstance()->start(fakeMain, 1); | |
| 239 | + QCoreApplication::exec(); | |
| 240 | + | |
| 215 | 241 | br_finalize(); |
| 216 | - return 0; | |
| 217 | 242 | } | ... | ... |
scripts/evalFaceRecognition-MEDS.sh
scripts/trainAgeRegression-PCSO.sh
| ... | ... | @@ -4,8 +4,8 @@ if [ ! -f trainAgeRegression-PCSO.sh ]; then |
| 4 | 4 | exit |
| 5 | 5 | fi |
| 6 | 6 | |
| 7 | -#rm -f ../models/features/FaceClassificationRegistration | |
| 8 | -#rm -f ../models/features/FaceClassificationExtraction | |
| 9 | -rm -f ../models/algorithms/AgeRegression | |
| 7 | +#rm -f ../share/openbr/models/features/FaceClassificationRegistration | |
| 8 | +#rm -f ../share/openbr/models/features/FaceClassificationExtraction | |
| 9 | +rm -f ../share/openbr/models/algorithms/AgeRegression | |
| 10 | 10 | |
| 11 | 11 | br -algorithm AgeRegression -path ../data/PCSO/Images -train "../data/PCSO/PCSO.db[query='SELECT File,Age,PersonID FROM PCSO WHERE Age >= 15 AND AGE <= 75', subset=0:200]" ../share/openbr/models/algorithms/AgeRegression | ... | ... |
scripts/trainFaceRecognition-PCSO.sh
| ... | ... | @@ -4,10 +4,10 @@ if [ ! -f trainFaceRecognition-PCSO.sh ]; then |
| 4 | 4 | exit |
| 5 | 5 | fi |
| 6 | 6 | |
| 7 | -#rm -f ../models/features/FaceRecognitionRegistration | |
| 8 | -#rm -f ../models/features/FaceRecognitionExtraction | |
| 9 | -#rm -f ../models/features/FaceRecognitionEmbedding | |
| 10 | -#rm -f ../models/features/FaceRecognitionQuantization | |
| 11 | -rm -f ../models/algorithms/FaceRecognition | |
| 7 | +#rm -f ../share/openbr/models/features/FaceRecognitionRegistration | |
| 8 | +#rm -f ../share/openbr/models/features/FaceRecognitionExtraction | |
| 9 | +#rm -f ../share/openbr/models/features/FaceRecognitionEmbedding | |
| 10 | +#rm -f ../share/openbr/models/features/FaceRecognitionQuantization | |
| 11 | +rm -f ../share/openbr/models/algorithms/FaceRecognition | |
| 12 | 12 | |
| 13 | 13 | br -algorithm FaceRecognition -path ../data/PCSO/Images -train "../data/PCSO/PCSO.db[query='SELECT File,'S'||PersonID,PersonID FROM PCSO', subset=0:5:6000]" ../share/openbr/models/algorithms/FaceRecognition | ... | ... |
scripts/trainGenderClassification-PCSO.sh
| ... | ... | @@ -4,8 +4,8 @@ if [ ! -f trainGenderClassification-PCSO.sh ]; then |
| 4 | 4 | exit |
| 5 | 5 | fi |
| 6 | 6 | |
| 7 | -#rm -f ../models/features/FaceClassificationRegistration | |
| 8 | -#rm -f ../models/features/FaceClassificationExtraction | |
| 9 | -rm -f ../models/algorithms/GenderClassification | |
| 7 | +#rm -f ../share/openbr/models/features/FaceClassificationRegistration | |
| 8 | +#rm -f ../share/openbr/models/features/FaceClassificationExtraction | |
| 9 | +rm -f ../share/openbr/models/algorithms/GenderClassification | |
| 10 | 10 | |
| 11 | 11 | br -algorithm GenderClassification -path ../data/PCSO/Images -train "../data/PCSO/PCSO.db[query='SELECT File,Gender,PersonID FROM PCSO', subset=0:8000]" ../share/openbr/models/algorithms/GenderClassification | ... | ... |
scripts/trainImageRetrieval-LFW.sh
0 โ 100755
| 1 | +#!/bin/bash | |
| 2 | +if [ ! -f trainImageRetrieval-LFW.sh ]; then | |
| 3 | + echo "Run this script from the scripts folder!" | |
| 4 | + exit | |
| 5 | +fi | |
| 6 | + | |
| 7 | +if [ ! -e Algorithm_Dataset ]; then | |
| 8 | + mkdir Algorithm_Dataset | |
| 9 | +fi | |
| 10 | + | |
| 11 | +if [ ! -e LFW.mask ]; then | |
| 12 | + br -makeMask ~/lfw2[step=10] . LFW.mask | |
| 13 | +fi | |
| 14 | + | |
| 15 | +rm -f ../share/openbr/models/algorithms/ImageRetrieval | |
| 16 | + | |
| 17 | +br -algorithm ImageRetrieval -train ~/lfw2[step=10] ../share/openbr/models/algorithms/ImageRetrieval | |
| 18 | +br -algorithm ImageRetrieval -compare ~/lfw2[step=10] . ImageRetrieval_LFW.mtx -eval ImageRetrieval_LFW.mtx LFW.mask Algorithm_Dataset/ImageRetrieval_LFW.csv | |
| 19 | +br -plot Algorithm_Dataset/*_LFW.csv LFW | |
| 20 | + | |
| 21 | +#rm -f ../share/openbr/models/algorithms/ImageRetrieval | ... | ... |
sdk/core/bee.cpp
| ... | ... | @@ -236,7 +236,7 @@ void BEE::makeMask(const QString &targetInput, const QString &queryInput, const |
| 236 | 236 | qDebug("Making mask from %s and %s to %s", qPrintable(targetInput), qPrintable(queryInput), qPrintable(mask)); |
| 237 | 237 | |
| 238 | 238 | FileList targetFiles = TemplateList::fromGallery(targetInput).files(); |
| 239 | - FileList queryFiles = TemplateList::fromGallery(queryInput).files(); | |
| 239 | + FileList queryFiles = (queryInput == ".") ? targetFiles : TemplateList::fromGallery(queryInput).files(); | |
| 240 | 240 | QList<float> targetLabels = targetFiles.labels(); |
| 241 | 241 | QList<float> queryLabels = queryFiles.labels(); |
| 242 | 242 | QList<int> targetPartitions = targetFiles.crossValidationPartitions(); | ... | ... |
sdk/core/opencvutils.cpp
| ... | ... | @@ -143,6 +143,7 @@ Mat OpenCVUtils::toMatByRow(const QList<Mat> &src) |
| 143 | 143 | |
| 144 | 144 | int rows = 0; foreach (const Mat &m, src) rows += m.rows; |
| 145 | 145 | int cols = src.first().cols; |
| 146 | + if (cols == 0) qFatal("Columnless matrix!"); | |
| 146 | 147 | int type = src.first().type(); |
| 147 | 148 | Mat dst(rows, cols, type); |
| 148 | 149 | |
| ... | ... | @@ -172,22 +173,6 @@ QString OpenCVUtils::elemToString(const Mat &m, int r, int c) |
| 172 | 173 | return "?"; |
| 173 | 174 | } |
| 174 | 175 | |
| 175 | -float OpenCVUtils::elemToFloat(const Mat &m, int r, int c) | |
| 176 | -{ | |
| 177 | - assert(m.channels() == 1); | |
| 178 | - switch (m.depth()) { | |
| 179 | - case CV_8U: return float(m.at<quint8>(r,c)); | |
| 180 | - case CV_8S: return float(m.at<qint8>(r,c)); | |
| 181 | - case CV_16U: return float(m.at<quint16>(r,c)); | |
| 182 | - case CV_16S: return float(m.at<qint16>(r,c)); | |
| 183 | - case CV_32S: return float(m.at<qint32>(r,c)); | |
| 184 | - case CV_32F: return float(m.at<float>(r,c)); | |
| 185 | - case CV_64F: return float(m.at<double>(r,c)); | |
| 186 | - default: qFatal("Unknown matrix depth"); | |
| 187 | - } | |
| 188 | - return 0; | |
| 189 | -} | |
| 190 | - | |
| 191 | 176 | QString OpenCVUtils::matrixToString(const Mat &m) |
| 192 | 177 | { |
| 193 | 178 | QString result; |
| ... | ... | @@ -224,18 +209,6 @@ QStringList OpenCVUtils::matrixToStringList(const Mat &m) |
| 224 | 209 | return results; |
| 225 | 210 | } |
| 226 | 211 | |
| 227 | -QList<float> OpenCVUtils::matrixToVector(const Mat &m) | |
| 228 | -{ | |
| 229 | - QList<float> results; | |
| 230 | - vector<Mat> mv; | |
| 231 | - split(m, mv); | |
| 232 | - foreach (const Mat &mc, mv) | |
| 233 | - for (int i=0; i<mc.rows; i++) | |
| 234 | - for (int j=0; j<mc.cols; j++) | |
| 235 | - results.append(elemToFloat(mc, i, j)); | |
| 236 | - return results; | |
| 237 | -} | |
| 238 | - | |
| 239 | 212 | Point2f OpenCVUtils::toPoint(const QPointF &qPoint) |
| 240 | 213 | { |
| 241 | 214 | return Point2f(qPoint.x(), qPoint.y()); | ... | ... |
sdk/core/opencvutils.h
| ... | ... | @@ -40,10 +40,38 @@ namespace OpenCVUtils |
| 40 | 40 | |
| 41 | 41 | // From image |
| 42 | 42 | QString elemToString(const cv::Mat &m, int r, int c); |
| 43 | - float elemToFloat(const cv::Mat &m, int r, int c); | |
| 44 | 43 | QString matrixToString(const cv::Mat &m); |
| 45 | 44 | QStringList matrixToStringList(const cv::Mat &m); |
| 46 | - QList<float> matrixToVector(const cv::Mat &m); | |
| 45 | + | |
| 46 | + template <typename T> | |
| 47 | + T getElement(const cv::Mat &m, int r, int c) | |
| 48 | + { | |
| 49 | + assert(m.channels() == 1); | |
| 50 | + switch (m.depth()) { | |
| 51 | + case CV_8U: return T(m.at<quint8>(r,c)); | |
| 52 | + case CV_8S: return T(m.at<qint8>(r,c)); | |
| 53 | + case CV_16U: return T(m.at<quint16>(r,c)); | |
| 54 | + case CV_16S: return T(m.at<qint16>(r,c)); | |
| 55 | + case CV_32S: return T(m.at<qint32>(r,c)); | |
| 56 | + case CV_32F: return T(m.at<float>(r,c)); | |
| 57 | + case CV_64F: return T(m.at<double>(r,c)); | |
| 58 | + default: qFatal("Unknown matrix depth!"); | |
| 59 | + } | |
| 60 | + return 0; | |
| 61 | + } | |
| 62 | + | |
| 63 | + template <typename T> | |
| 64 | + QList<T> matrixToVector(const cv::Mat &m) | |
| 65 | + { | |
| 66 | + QList<T> results; | |
| 67 | + std::vector<cv::Mat> mv; | |
| 68 | + cv::split(m, mv); | |
| 69 | + foreach (const cv::Mat &mc, mv) | |
| 70 | + for (int i=0; i<mc.rows; i++) | |
| 71 | + for (int j=0; j<mc.cols; j++) | |
| 72 | + results.append(getElement<float>(mc, i, j)); | |
| 73 | + return results; | |
| 74 | + } | |
| 47 | 75 | |
| 48 | 76 | // Conversions |
| 49 | 77 | cv::Point2f toPoint(const QPointF &qPoint); | ... | ... |
sdk/core/qtutils.cpp
| ... | ... | @@ -39,7 +39,7 @@ QStringList QtUtils::getFiles(QDir dir, bool recursive) |
| 39 | 39 | |
| 40 | 40 | QStringList files; |
| 41 | 41 | foreach (const QString &file, NaturalStringSort(dir.entryList(QDir::Files))) |
| 42 | - files.append(QDir::cleanPath(dir.absoluteFilePath(file))); | |
| 42 | + files.append(dir.absoluteFilePath(file)); | |
| 43 | 43 | |
| 44 | 44 | if (!recursive) return files; |
| 45 | 45 | ... | ... |
sdk/core/qtutils.h
sdk/frvt2012.cpp
| ... | ... | @@ -29,7 +29,12 @@ static const int frvt2012_template_size = 768; |
| 29 | 29 | |
| 30 | 30 | static void initialize(const string &configuration_location) |
| 31 | 31 | { |
| 32 | - if (Globals == NULL) Context::initialize(0, NULL, QString::fromStdString(configuration_location)); | |
| 32 | + // Fake the command line arguments | |
| 33 | + int argc = 1; | |
| 34 | + char arg1[1]; arg1[0]='\0'; | |
| 35 | + char *argv[] = { arg1 }; | |
| 36 | + | |
| 37 | + if (Globals == NULL) Context::initialize(argc, argv, QString::fromStdString(configuration_location)); | |
| 33 | 38 | Globals->quiet = true; |
| 34 | 39 | Globals->parallelism = 0; |
| 35 | 40 | } | ... | ... |
sdk/openbr.cpp
| ... | ... | @@ -103,7 +103,7 @@ void br_fuse(int num_input_simmats, const char *input_simmats[], const char *mas |
| 103 | 103 | Fuse(QtUtils::toStringList(num_input_simmats, input_simmats), mask, normalization, fusion, output_simmat); |
| 104 | 104 | } |
| 105 | 105 | |
| 106 | -void br_initialize(int argc, char *argv[], const char *sdk_path) | |
| 106 | +void br_initialize(int &argc, char *argv[], const char *sdk_path) | |
| 107 | 107 | { |
| 108 | 108 | Context::initialize(argc, argv, sdk_path); |
| 109 | 109 | } | ... | ... |
sdk/openbr.h
| ... | ... | @@ -211,7 +211,7 @@ BR_EXPORT void br_fuse(int num_input_simmats, const char *input_simmats[], const |
| 211 | 211 | * \brief Wraps br::Context::initialize() |
| 212 | 212 | * \see br_initialize_qt br_finalize |
| 213 | 213 | */ |
| 214 | -BR_EXPORT void br_initialize(int argc, char *argv[], const char *sdk_path = ""); | |
| 214 | +BR_EXPORT void br_initialize(int &argc, char *argv[], const char *sdk_path = ""); | |
| 215 | 215 | |
| 216 | 216 | /*! |
| 217 | 217 | * \brief Wraps br::Context::initializeQt() | ... | ... |
sdk/openbr_export.cpp
| ... | ... | @@ -82,6 +82,7 @@ $ br -help |
| 82 | 82 | * -# Consider the free open source program <a href="http://wincdemu.sysprogs.org">WinCDEmu</a> if you need a program to mount ISO images. |
| 83 | 83 | * -# You will have to register with Microsoft after installation, but it's free. |
| 84 | 84 | * -# Grab any available <a href="http://www.microsoft.com/visualstudio/eng/downloads#d-visual-studio-2012-update">Visual Studio Updates</a>. |
| 85 | + * -# Download and install <a href="http://msdn.microsoft.com/en-us/windows/hardware/hh852363.aspx">Windows 8 SDK</a>. | |
| 85 | 86 | * -# <a href="http://www.cmake.org/files/v2.8/cmake-2.8.10.2-win32-x86.exe">Download CMake 2.8.10.2</a> and install. |
| 86 | 87 | * -# During installation setup select "add CMake to PATH". |
| 87 | 88 | * -# <a href="http://downloads.sourceforge.net/project/opencvlibrary/opencv-unix/2.4.4/OpenCV-2.4.4.tar.bz2">Download OpenCV 2.4.4</a>. |
| ... | ... | @@ -100,19 +101,8 @@ $ br -help |
| 100 | 101 | * $ nmake install |
| 101 | 102 | * $ nmake clean |
| 102 | 103 | * \endcode |
| 103 | - * -# <a href="http://releases.qt-project.org/qt5/5.0.1/single/qt-everywhere-opensource-src-5.0.1.zip">Download Qt 5.0.1</a> and unzip. | |
| 104 | - * -# Install Perl/Python/Ruby dependencies as explained in the "Windows" section of "README". Make sure they are added to "path" when given the option during installation. | |
| 104 | + * -# <a href="http://releases.qt-project.org/digia/5.0.1/backups/2013-01-18-412/qt-windows-opensource-5.0.1-msvc2012_64-x86_64-offline-2013-01-18-412.exe">Download Qt 5.0.1</a> and install. | |
| 105 | 105 | * -# <a href="http://www.microsoft.com/en-us/download/confirmation.aspx?id=6812">Download Direct X Software Developement Kit</a> and install. |
| 106 | - * -# From the VS2012 x64 Cross Tools Command Prompt: | |
| 107 | - * \code | |
| 108 | - * $ cd qt-everywhere-opensource-src-5.0.1 | |
| 109 | - * $ configure -prefix C:\Qt\5.0.1\msvc2012 -opensource -confirm-license -nomake examples -nomake tests | |
| 110 | - * $ nmake | |
| 111 | - * $ nmake install | |
| 112 | - * $ cd .. | |
| 113 | - * $ rmdir /Q /S qt-everywhere-opensource-src-5.0.1 | |
| 114 | - * \endcode | |
| 115 | - * -# nmake will take several hours to finish. | |
| 116 | 106 | * -# Create a <a href="github.com">GitHub</a> account and follow their instructions for <a href="https://help.github.com/articles/set-up-git">setting up Git</a>. |
| 117 | 107 | * -# Launch "Git Bash" from the Desktop and clone OpenBR: |
| 118 | 108 | * \code |
| ... | ... | @@ -128,7 +118,7 @@ $ br -help |
| 128 | 118 | * $ cd C:\openbr |
| 129 | 119 | * $ mkdir build-msvc2012 |
| 130 | 120 | * $ cd build-msvc2012 |
| 131 | - * $ cmake -G "CodeBlocks - NMake Makefiles" -DCMAKE_PREFIX_PATH="C:/OpenCV-2.4.4/build-msvc2012/install;C:/Qt/5.0.1/msvc2012" -DCMAKE_INSTALL_PREFIX="./install" -DBR_INSTALL_DEPENDENCIES=ON -DCMAKE_BUILD_TYPE=Release .. | |
| 121 | + * $ cmake -G "CodeBlocks - NMake Makefiles" -DCMAKE_PREFIX_PATH="C:/OpenCV-2.4.4/build-msvc2012/install;C:/Qt/Qt5.0.1/5.0.1/msvc2012_64" -DCMAKE_INSTALL_PREFIX="./install" -DBR_INSTALL_DEPENDENCIES=ON -DCMAKE_BUILD_TYPE=Release .. | |
| 132 | 122 | * $ nmake |
| 133 | 123 | * $ nmake install |
| 134 | 124 | * \endcode |
| ... | ... | @@ -147,7 +137,7 @@ $ br -help |
| 147 | 137 | * -# Browse to your prexisting build directory "C:\openbr\build-msvc2012" then select "Next". |
| 148 | 138 | * -# Clear any text in the "arguments" box then select "Run CMake" then "Finish". |
| 149 | 139 | * -# You're all set! You can find more information on Qt Creator <a href="http://qt-project.org/doc/qtcreator-2.6/">here</a> if you need. |
| 150 | - * -# Package OpenBR! | |
| 140 | + * -# (Optional) Package OpenBR! | |
| 151 | 141 | * -# <a href="http://sourceforge.net/projects/nsis/files/NSIS%202/2.46/nsis-2.46-setup.exe/download?use_mirror=iweb&download=">Download NSIS 2.46</a> and install. |
| 152 | 142 | * -# From the VS2012 x64 Cross Tools Command Prompt: |
| 153 | 143 | * \code |
| ... | ... | @@ -226,7 +216,7 @@ $ br -help |
| 226 | 216 | * -# Browse to your prexisting build directory "C:\openbr\build-mingw64" then select "Next". |
| 227 | 217 | * -# Clear any text in the "arguments" box then select "Run CMake" then "Finish". |
| 228 | 218 | * -# You're all set! You can find more information on Qt Creator <a href="http://qt-project.org/doc/qtcreator-2.6/">here</a> if you need. |
| 229 | - * -# Package OpenBR! | |
| 219 | + * -# (Optional) Package OpenBR! | |
| 230 | 220 | * -# <a href="http://sourceforge.net/projects/nsis/files/NSIS%202/2.46/nsis-2.46-setup.exe/download?use_mirror=iweb&download=">Download NSIS 2.46</a> and install. |
| 231 | 221 | * -# From the MinGW-w64 Command Prompt: |
| 232 | 222 | * \code |
| ... | ... | @@ -277,7 +267,7 @@ $ br -help |
| 277 | 267 | * $ cd build |
| 278 | 268 | * $ cmake -DCMAKE_PREFIX_PATH=~/Qt5.0.1/5.0.1/clang_64 -DCMAKE_BUILD_TYPE=Release .. |
| 279 | 269 | * $ make -j4 |
| 280 | - * $ make install | |
| 270 | + * $ sudo make install | |
| 281 | 271 | * \endcode |
| 282 | 272 | * -# Hack OpenBR! |
| 283 | 273 | * -# Open Qt Creator IDE |
| ... | ... | @@ -289,12 +279,12 @@ $ br -help |
| 289 | 279 | * -# Browse to your prexisting build directory "~/openbr/build" then select "Continue". |
| 290 | 280 | * -# Select "Run CMake" then "Done". |
| 291 | 281 | * -# You're all set! You can find more information on Qt Creator <a href="http://qt-project.org/doc/qtcreator">here</a> if you need. |
| 292 | - * -# Package OpenBR! | |
| 282 | + * -# (Optional) Package OpenBR! | |
| 293 | 283 | * \code |
| 294 | 284 | * $ cd openbr/build |
| 295 | 285 | * $ make package |
| 296 | 286 | * \endcode |
| 297 | - * -# Build OpenBR documentation! | |
| 287 | + * -# (Optional) Build OpenBR documentation! | |
| 298 | 288 | * -# <a href="ftp://ftp.stack.nl/pub/users/dimitri/doxygen-1.8.2.src.tar.gz">Download Doxygen 1.8.2</a> and install: |
| 299 | 289 | * \code |
| 300 | 290 | * $ cd ~/Downloads |
| ... | ... | @@ -367,7 +357,7 @@ $ br -help |
| 367 | 357 | * $ cd build |
| 368 | 358 | * $ cmake -DCMAKE_PREFIX_PATH=~/Qt5.0.1/5.0.1/gcc_64 -DCMAKE_BUILD_TYPE=Release .. |
| 369 | 359 | * $ make -j4 |
| 370 | - * $ make install | |
| 360 | + * $ sudo make install | |
| 371 | 361 | * \endcode |
| 372 | 362 | * -# Hack OpenBR! |
| 373 | 363 | * -# Open Qt Creator IDE |
| ... | ... | @@ -379,7 +369,7 @@ $ br -help |
| 379 | 369 | * -# Browse to your prexisting build directory "~/openbr/build" then select "Next". |
| 380 | 370 | * -# Select "Run CMake" then "Finish". |
| 381 | 371 | * -# You're all set! You can find more information on Qt Creator <a href="http://qt-project.org/doc/qtcreator-2.6/">here</a> if you need. |
| 382 | - * -# Package OpenBR! | |
| 372 | + * -# (Optional) Package OpenBR! | |
| 383 | 373 | * \code |
| 384 | 374 | * $ cd openbr/build |
| 385 | 375 | * $ make package |
| ... | ... | @@ -434,7 +424,7 @@ $ br -help |
| 434 | 424 | * $ cd build-icc |
| 435 | 425 | * $ cmake -DCMAKE_C_COMPILER=/opt/intel/bin/icc -DCMAKE_CXX_COMPILER=/opt/intel/bin/icpc -DCMAKE_PREFIX_PATH=~/Qt5.0.1/5.0.1/gcc_64 -DCMAKE_BUILD_TYPE=Release .. |
| 436 | 426 | * $ make -j4 |
| 437 | - * $ make install | |
| 427 | + * $ sudo make install | |
| 438 | 428 | * \endcode |
| 439 | 429 | * -# Hack OpenBR! |
| 440 | 430 | * -# Open Qt Creator IDE |
| ... | ... | @@ -446,7 +436,7 @@ $ br -help |
| 446 | 436 | * -# Browse to your prexisting build directory "~/openbr/build" then select "Next". |
| 447 | 437 | * -# Select "Run CMake" then "Finish". |
| 448 | 438 | * -# You're all set! You can find more information on Qt Creator <a href="http://qt-project.org/doc/qtcreator-2.6/">here</a> if you need. |
| 449 | - * -# Package OpenBR! | |
| 439 | + * -# (Optional) Package OpenBR! | |
| 450 | 440 | * \code |
| 451 | 441 | * $ cd openbr/build |
| 452 | 442 | * $ make package | ... | ... |
sdk/openbr_plugin.cpp
| ... | ... | @@ -14,6 +14,7 @@ |
| 14 | 14 | * limitations under the License. * |
| 15 | 15 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ |
| 16 | 16 | |
| 17 | +#include <QFutureSynchronizer> | |
| 17 | 18 | #include <QMetaProperty> |
| 18 | 19 | #include <QPointF> |
| 19 | 20 | #include <QRect> |
| ... | ... | @@ -28,6 +29,10 @@ |
| 28 | 29 | #include <iostream> |
| 29 | 30 | #include <openbr_plugin.h> |
| 30 | 31 | |
| 32 | +#ifndef BR_EMBEDDED | |
| 33 | +#include <QApplication> | |
| 34 | +#endif | |
| 35 | + | |
| 31 | 36 | #include "version.h" |
| 32 | 37 | #include "core/bee.h" |
| 33 | 38 | #include "core/common.h" |
| ... | ... | @@ -385,6 +390,15 @@ TemplateList TemplateList::fromGallery(const br::File &gallery) |
| 385 | 390 | QScopedPointer<Gallery> i(Gallery::make(file)); |
| 386 | 391 | TemplateList newTemplates = i->read(); |
| 387 | 392 | newTemplates = newTemplates.mid(gallery.get<int>("pos", 0), gallery.get<int>("length", -1)); |
| 393 | + | |
| 394 | + const int step = gallery.get<int>("step", 1); | |
| 395 | + if (step > 1) { | |
| 396 | + TemplateList downsampled; downsampled.reserve(newTemplates.size()/step); | |
| 397 | + for (int i=0; i<newTemplates.size(); i+=step) | |
| 398 | + downsampled.append(newTemplates[i]); | |
| 399 | + newTemplates = downsampled; | |
| 400 | + } | |
| 401 | + | |
| 388 | 402 | if (gallery.get<bool>("reduce", false)) newTemplates = newTemplates.reduced(); |
| 389 | 403 | const int crossValidate = gallery.get<int>("crossValidate"); |
| 390 | 404 | if (crossValidate > 0) srand(0); |
| ... | ... | @@ -770,27 +784,31 @@ int br::Context::timeRemaining() const |
| 770 | 784 | return std::max(0, int((1 - p) / p * startTime.elapsed())) / 1000; |
| 771 | 785 | } |
| 772 | 786 | |
| 773 | -void br::Context::trackFutures(QList< QFuture<void> > &futures) | |
| 774 | -{ | |
| 775 | - foreach (QFuture<void> future, futures) { | |
| 776 | - QCoreApplication::processEvents(); | |
| 777 | - future.waitForFinished(); | |
| 778 | - } | |
| 779 | -} | |
| 780 | - | |
| 781 | 787 | bool br::Context::checkSDKPath(const QString &sdkPath) |
| 782 | 788 | { |
| 783 | 789 | return QFileInfo(sdkPath + "/share/openbr/openbr.bib").exists(); |
| 784 | 790 | } |
| 785 | 791 | |
| 786 | -void br::Context::initialize(int argc, char *argv[], const QString &sdkPath) | |
| 792 | +void br::Context::initialize(int &argc, char *argv[], const QString &sdkPath) | |
| 787 | 793 | { |
| 794 | + // We take in argc as a reference due to: | |
| 795 | + // https://bugreports.qt-project.org/browse/QTBUG-5637 | |
| 796 | + // QApplication should be initialized before anything else. | |
| 797 | + // Since we can't ensure that it gets deleted last, we never delete it. | |
| 798 | + static QCoreApplication *application = NULL; | |
| 799 | + if (application == NULL) { | |
| 800 | +#ifndef BR_EMBEDDED | |
| 801 | + application = new QApplication(argc, argv); | |
| 802 | +#else | |
| 803 | + application = new QCoreApplication(argc, argv); | |
| 804 | +#endif | |
| 805 | + } | |
| 806 | + | |
| 788 | 807 | if (Globals == NULL) { |
| 789 | 808 | Globals = new Context(); |
| 790 | 809 | Globals->init(File()); |
| 791 | 810 | } |
| 792 | 811 | |
| 793 | - Globals->coreApplication = QSharedPointer<QCoreApplication>(new QCoreApplication(argc, argv)); | |
| 794 | 812 | initializeQt(sdkPath); |
| 795 | 813 | |
| 796 | 814 | #ifdef BR_DISTRIBUTED |
| ... | ... | @@ -853,7 +871,10 @@ void br::Context::initializeQt(QString sdkPath) |
| 853 | 871 | |
| 854 | 872 | void br::Context::finalize() |
| 855 | 873 | { |
| 856 | - // Trigger registerd finalizers | |
| 874 | + // Is anyone still running? | |
| 875 | + QThreadPool::globalInstance()->waitForDone(); | |
| 876 | + | |
| 877 | + // Trigger registered finalizers | |
| 857 | 878 | QList< QSharedPointer<Initializer> > initializers = Factory<Initializer>::makeAll(); |
| 858 | 879 | foreach (const QSharedPointer<Initializer> &initializer, initializers) |
| 859 | 880 | initializer->finalize(); |
| ... | ... | @@ -883,6 +904,11 @@ QString br::Context::scratchPath() |
| 883 | 904 | |
| 884 | 905 | void br::Context::messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) |
| 885 | 906 | { |
| 907 | + // Something about this method is not thread safe, and will lead to crashes if qDebug | |
| 908 | + // statements are called from multiple threads. Unless we lock the whole thing... | |
| 909 | + static QMutex generalLock; | |
| 910 | + QMutexLocker locker(&generalLock); | |
| 911 | + | |
| 886 | 912 | QString txt; |
| 887 | 913 | switch (type) { |
| 888 | 914 | case QtDebugMsg: |
| ... | ... | @@ -904,11 +930,8 @@ void br::Context::messageHandler(QtMsgType type, const QMessageLogContext &conte |
| 904 | 930 | Globals->mostRecentMessage = txt; |
| 905 | 931 | |
| 906 | 932 | if (Globals->logFile.isWritable()) { |
| 907 | - static QMutex logLock; | |
| 908 | - logLock.lock(); | |
| 909 | 933 | Globals->logFile.write(qPrintable(txt)); |
| 910 | 934 | Globals->logFile.flush(); |
| 911 | - logLock.unlock(); | |
| 912 | 935 | } |
| 913 | 936 | |
| 914 | 937 | if (type == QtFatalMsg) { |
| ... | ... | @@ -917,8 +940,6 @@ void br::Context::messageHandler(QtMsgType type, const QMessageLogContext &conte |
| 917 | 940 | Globals->finalize(); |
| 918 | 941 | abort(); |
| 919 | 942 | } |
| 920 | - | |
| 921 | - QCoreApplication::processEvents(); // Used to retrieve messages before event loop starts | |
| 922 | 943 | } |
| 923 | 944 | |
| 924 | 945 | Context *br::Globals = NULL; |
| ... | ... | @@ -1147,14 +1168,12 @@ private: |
| 1147 | 1168 | for (int i=0; i<templatesList.size(); i++) |
| 1148 | 1169 | templatesList[i] = Downsample(templatesList[i], transforms[i]); |
| 1149 | 1170 | |
| 1150 | - QList< QFuture<void> > futures; | |
| 1151 | - const bool threaded = Globals->parallelism && (templatesList.size() > 1); | |
| 1171 | + QFutureSynchronizer<void> futures; | |
| 1152 | 1172 | for (int i=0; i<templatesList.size(); i++) { |
| 1153 | - if (threaded) futures.append(QtConcurrent::run(_train, transforms[i], &templatesList[i])); | |
| 1154 | - else _train (transforms[i], &templatesList[i]); | |
| 1173 | + if (Globals->parallelism) futures.addFuture(QtConcurrent::run(_train, transforms[i], &templatesList[i])); | |
| 1174 | + else _train (transforms[i], &templatesList[i]); | |
| 1155 | 1175 | } |
| 1156 | - | |
| 1157 | - if (threaded) Globals->trackFutures(futures); | |
| 1176 | + futures.waitForFinished(); | |
| 1158 | 1177 | } |
| 1159 | 1178 | |
| 1160 | 1179 | void project(const Template &src, Template &dst) const |
| ... | ... | @@ -1250,42 +1269,51 @@ Transform *Transform::clone() const |
| 1250 | 1269 | return clone; |
| 1251 | 1270 | } |
| 1252 | 1271 | |
| 1253 | -static void _backProject(const Transform *transform, const Template *dst, Template *src) | |
| 1272 | +static void _project(const Transform *transform, const Template *src, Template *dst) | |
| 1254 | 1273 | { |
| 1255 | 1274 | try { |
| 1256 | - transform->backProject(*dst, *src); | |
| 1275 | + transform->project(*src, *dst); | |
| 1257 | 1276 | } catch (...) { |
| 1258 | 1277 | qWarning("Exception triggered when processing %s with transform %s", qPrintable(src->file.flat()), qPrintable(transform->objectName())); |
| 1259 | - *src = Template(dst->file); | |
| 1260 | - src->file.set("FTE", true); | |
| 1278 | + *dst = Template(src->file); | |
| 1279 | + dst->file.set("FTE", true); | |
| 1261 | 1280 | } |
| 1262 | 1281 | } |
| 1263 | 1282 | |
| 1264 | - | |
| 1265 | - | |
| 1266 | -// Default project(TemplateList) -- call project(template) separately for each input | |
| 1267 | -// template | |
| 1283 | +// Default project(TemplateList) calls project(Template) separately for each element | |
| 1268 | 1284 | void Transform::project(const TemplateList &src, TemplateList &dst) const |
| 1269 | 1285 | { |
| 1270 | - dst.clear(); | |
| 1271 | - | |
| 1272 | - // Project templates derived from a single image, default implementation: project each | |
| 1273 | - // input template to an ouptut template individually. | |
| 1274 | - foreach(const Template & src_template, src) { | |
| 1275 | - dst.append(Template(src_template.file)); | |
| 1276 | - try { | |
| 1277 | - project(src_template, dst.back()); | |
| 1278 | - } catch (...) { | |
| 1279 | - qWarning("Exception triggered when processing %s with transform %s", qPrintable(src_template.file.flat()), qPrintable(objectName())); | |
| 1280 | - dst.back() = Template(src_template.file); | |
| 1281 | - dst.back().file.set("FTE", true); | |
| 1286 | + dst.reserve(src.size()); | |
| 1287 | + | |
| 1288 | + // There are certain conditions where we should process the templates in serial, | |
| 1289 | + // but generally we'd prefer to process them in parallel. | |
| 1290 | + if ((src.size() < 2) || | |
| 1291 | + (QThreadPool::globalInstance()->activeThreadCount() >= QThreadPool::globalInstance()->maxThreadCount()) || | |
| 1292 | + (Globals->parallelism == 0)) { | |
| 1293 | + | |
| 1294 | + foreach (const Template &t, src) { | |
| 1295 | + dst.append(Template()); | |
| 1296 | + _project(this, &t, &dst.last()); | |
| 1282 | 1297 | } |
| 1298 | + } else { | |
| 1299 | + for (int i=0; i<src.size(); i++) | |
| 1300 | + dst.append(Template()); | |
| 1301 | + QFutureSynchronizer<void> futures; | |
| 1302 | + for (int i=0; i<dst.size(); i++) | |
| 1303 | + futures.addFuture(QtConcurrent::run(_project, this, &src[i], &dst[i])); | |
| 1304 | + futures.waitForFinished(); | |
| 1283 | 1305 | } |
| 1284 | 1306 | } |
| 1285 | 1307 | |
| 1286 | -void Transform::backProject(const Template &dst, Template &src) const | |
| 1308 | +static void _backProject(const Transform *transform, const Template *dst, Template *src) | |
| 1287 | 1309 | { |
| 1288 | - src = dst; | |
| 1310 | + try { | |
| 1311 | + transform->backProject(*dst, *src); | |
| 1312 | + } catch (...) { | |
| 1313 | + qWarning("Exception triggered when processing %s with transform %s", qPrintable(src->file.flat()), qPrintable(transform->objectName())); | |
| 1314 | + *src = Template(dst->file); | |
| 1315 | + src->file.set("FTE", true); | |
| 1316 | + } | |
| 1289 | 1317 | } |
| 1290 | 1318 | |
| 1291 | 1319 | void Transform::backProject(const TemplateList &dst, TemplateList &src) const |
| ... | ... | @@ -1293,12 +1321,11 @@ void Transform::backProject(const TemplateList &dst, TemplateList &src) const |
| 1293 | 1321 | src.reserve(dst.size()); |
| 1294 | 1322 | for (int i=0; i<dst.size(); i++) src.append(Template()); |
| 1295 | 1323 | |
| 1296 | - QList< QFuture<void> > futures; | |
| 1297 | - if (Globals->parallelism) futures.reserve(dst.size()); | |
| 1324 | + QFutureSynchronizer<void> futures; | |
| 1298 | 1325 | for (int i=0; i<dst.size(); i++) |
| 1299 | - if (Globals->parallelism) futures.append(QtConcurrent::run(_backProject, this, &dst[i], &src[i])); | |
| 1300 | - else _backProject (this, &dst[i], &src[i]); | |
| 1301 | - if (Globals->parallelism) Globals->trackFutures(futures); | |
| 1326 | + if (Globals->parallelism) futures.addFuture(QtConcurrent::run(_backProject, this, &dst[i], &src[i])); | |
| 1327 | + else _backProject (this, &dst[i], &src[i]); | |
| 1328 | + futures.waitForFinished(); | |
| 1302 | 1329 | } |
| 1303 | 1330 | |
| 1304 | 1331 | /* Distance - public methods */ |
| ... | ... | @@ -1326,16 +1353,16 @@ void Distance::compare(const TemplateList &target, const TemplateList &query, Ou |
| 1326 | 1353 | const bool stepTarget = target.size() > query.size(); |
| 1327 | 1354 | const int totalSize = std::max(target.size(), query.size()); |
| 1328 | 1355 | int stepSize = ceil(float(totalSize) / float(std::max(1, abs(Globals->parallelism)))); |
| 1329 | - QList< QFuture<void> > futures; futures.reserve(ceil(float(totalSize)/float(stepSize))); | |
| 1356 | + QFutureSynchronizer<void> futures; | |
| 1330 | 1357 | for (int i=0; i<totalSize; i+=stepSize) { |
| 1331 | 1358 | const TemplateList &targets(stepTarget ? TemplateList(target.mid(i, stepSize)) : target); |
| 1332 | 1359 | const TemplateList &queries(stepTarget ? query : TemplateList(query.mid(i, stepSize))); |
| 1333 | 1360 | const int targetOffset = stepTarget ? i : 0; |
| 1334 | 1361 | const int queryOffset = stepTarget ? 0 : i; |
| 1335 | - if (Globals->parallelism) futures.append(QtConcurrent::run(this, &Distance::compareBlock, targets, queries, output, targetOffset, queryOffset)); | |
| 1336 | - else compareBlock (targets, queries, output, targetOffset, queryOffset); | |
| 1362 | + if (Globals->parallelism) futures.addFuture(QtConcurrent::run(this, &Distance::compareBlock, targets, queries, output, targetOffset, queryOffset)); | |
| 1363 | + else compareBlock (targets, queries, output, targetOffset, queryOffset); | |
| 1337 | 1364 | } |
| 1338 | - if (Globals->parallelism) Globals->trackFutures(futures); | |
| 1365 | + futures.waitForFinished(); | |
| 1339 | 1366 | } |
| 1340 | 1367 | |
| 1341 | 1368 | QList<float> Distance::compare(const TemplateList &targets, const Template &query) const | ... | ... |
sdk/openbr_plugin.h
| ... | ... | @@ -17,7 +17,6 @@ |
| 17 | 17 | #ifndef __OPENBR_PLUGIN_H |
| 18 | 18 | #define __OPENBR_PLUGIN_H |
| 19 | 19 | |
| 20 | -#include <QCoreApplication> | |
| 21 | 20 | #include <QDataStream> |
| 22 | 21 | #include <QDebug> |
| 23 | 22 | #include <QDir> |
| ... | ... | @@ -466,7 +465,6 @@ private: |
| 466 | 465 | class BR_EXPORT Context : public Object |
| 467 | 466 | { |
| 468 | 467 | Q_OBJECT |
| 469 | - QSharedPointer<QCoreApplication> coreApplication; | |
| 470 | 468 | QFile logFile; |
| 471 | 469 | |
| 472 | 470 | public: |
| ... | ... | @@ -624,12 +622,6 @@ public: |
| 624 | 622 | int timeRemaining() const; |
| 625 | 623 | |
| 626 | 624 | /*! |
| 627 | - * \brief Continues to print the progress of the futures until they are completed. | |
| 628 | - * \param futures The list of futures to track. | |
| 629 | - */ | |
| 630 | - void trackFutures(QList< QFuture<void> > &futures); | |
| 631 | - | |
| 632 | - /*! | |
| 633 | 625 | * \brief Returns \c true if \em sdkPath is valid, \c false otherwise. |
| 634 | 626 | * \param sdkPath The path to <tt>share/openbr/openbr.bib</tt> |
| 635 | 627 | */ |
| ... | ... | @@ -658,7 +650,7 @@ public: |
| 658 | 650 | * \note <a href="http://qt-project.org/">Qt</a> users should instead call initializeQt(). |
| 659 | 651 | * \see initializeQt finalize |
| 660 | 652 | */ |
| 661 | - static void initialize(int argc, char *argv[], const QString &sdkPath = ""); | |
| 653 | + static void initialize(int &argc, char *argv[], const QString &sdkPath = ""); | |
| 662 | 654 | |
| 663 | 655 | /*! |
| 664 | 656 | * \brief Alternative to initialize() for <a href="http://qt-project.org/">Qt</a> users. |
| ... | ... | @@ -998,7 +990,7 @@ public: |
| 998 | 990 | virtual void train(const TemplateList &data) = 0; /*!< \brief Train the transform. */ |
| 999 | 991 | virtual void project(const Template &src, Template &dst) const = 0; /*!< \brief Apply the transform. */ |
| 1000 | 992 | virtual void project(const TemplateList &src, TemplateList &dst) const; /*!< \brief Apply the transform. */ |
| 1001 | - virtual void backProject(const Template &dst, Template &src) const; /*!< \brief Invert the transform. */ | |
| 993 | + virtual void backProject(const Template &dst, Template &src) const { src = dst; } /*!< \brief Invert the transform. */ | |
| 1002 | 994 | virtual void backProject(const TemplateList &dst, TemplateList &src) const; /*!< \brief Invert the transform. */ |
| 1003 | 995 | |
| 1004 | 996 | /*!< \brief Apply the transform, may update the transform's internal state */ |
| ... | ... | @@ -1113,6 +1105,7 @@ class BR_EXPORT TimeVaryingTransform : public Transform |
| 1113 | 1105 | { |
| 1114 | 1106 | Q_OBJECT |
| 1115 | 1107 | |
| 1108 | +public: | |
| 1116 | 1109 | virtual bool timeVarying() const { return true; } |
| 1117 | 1110 | |
| 1118 | 1111 | virtual void project(const Template &src, Template &dst) const |
| ... | ... | @@ -1127,6 +1120,23 @@ class BR_EXPORT TimeVaryingTransform : public Transform |
| 1127 | 1120 | (void) dst; (void) src; |
| 1128 | 1121 | } |
| 1129 | 1122 | |
| 1123 | + // Get a compile failure if this isn't here to go along with the other | |
| 1124 | + // projectUpdate, no idea why | |
| 1125 | + virtual void projectUpdate(const Template & src, Template & dst) | |
| 1126 | + { | |
| 1127 | + (void) src; (void) dst; | |
| 1128 | + qFatal("do something useful"); | |
| 1129 | + } | |
| 1130 | + | |
| 1131 | + virtual void projectUpdate(const TemplateList &src, TemplateList &dst) | |
| 1132 | + { | |
| 1133 | + foreach (const Template & src_part, src) { | |
| 1134 | + Template out; | |
| 1135 | + projectUpdate(src_part, out); | |
| 1136 | + dst.append(out); | |
| 1137 | + } | |
| 1138 | + } | |
| 1139 | + | |
| 1130 | 1140 | protected: |
| 1131 | 1141 | TimeVaryingTransform(bool independent = true, bool trainable = true) : Transform(independent, trainable) {} |
| 1132 | 1142 | }; |
| ... | ... | @@ -1170,6 +1180,51 @@ protected: |
| 1170 | 1180 | UntrainableMetaTransform() : UntrainableTransform(false) {} |
| 1171 | 1181 | }; |
| 1172 | 1182 | |
| 1183 | +/*! | |
| 1184 | + * \brief A MetaTransform that aggregates some sub-transforms | |
| 1185 | + */ | |
| 1186 | +class BR_EXPORT CompositeTransform : public TimeVaryingTransform | |
| 1187 | +{ | |
| 1188 | + Q_OBJECT | |
| 1189 | + | |
| 1190 | +public: | |
| 1191 | + Q_PROPERTY(QList<br::Transform*> transforms READ get_transforms WRITE set_transforms RESET reset_transforms) | |
| 1192 | + BR_PROPERTY(QList<br::Transform*>, transforms, QList<br::Transform*>()) | |
| 1193 | + | |
| 1194 | + virtual void project(const Template &src, Template &dst) const | |
| 1195 | + { | |
| 1196 | + if (timeVarying()) qFatal("No const project defined for time-varying transform"); | |
| 1197 | + _project(src, dst); | |
| 1198 | + } | |
| 1199 | + | |
| 1200 | + virtual void project(const TemplateList &src, TemplateList &dst) const | |
| 1201 | + { | |
| 1202 | + if (timeVarying()) qFatal("No const project defined for time-varying transform"); | |
| 1203 | + _project(src, dst); | |
| 1204 | + } | |
| 1205 | + | |
| 1206 | + bool timeVarying() const { return isTimeVarying; } | |
| 1207 | + | |
| 1208 | + void init() | |
| 1209 | + { | |
| 1210 | + isTimeVarying = false; | |
| 1211 | + foreach (const br::Transform *transform, transforms) { | |
| 1212 | + if (transform->timeVarying()) { | |
| 1213 | + isTimeVarying = true; | |
| 1214 | + break; | |
| 1215 | + } | |
| 1216 | + } | |
| 1217 | + } | |
| 1218 | + | |
| 1219 | +protected: | |
| 1220 | + bool isTimeVarying; | |
| 1221 | + | |
| 1222 | + virtual void _project(const Template & src, Template & dst) const = 0; | |
| 1223 | + virtual void _project(const TemplateList & src, TemplateList & dst) const = 0; | |
| 1224 | + | |
| 1225 | + CompositeTransform() : TimeVaryingTransform(false) {} | |
| 1226 | +}; | |
| 1227 | + | |
| 1173 | 1228 | /*! @}*/ |
| 1174 | 1229 | |
| 1175 | 1230 | /*! | ... | ... |
sdk/plugins/algorithms.cpp
| ... | ... | @@ -32,7 +32,6 @@ class AlgorithmsInitializer : public Initializer |
| 32 | 32 | { |
| 33 | 33 | // Face |
| 34 | 34 | Globals->abbreviations.insert("FaceRecognition", "FaceDetection!<FaceRecognitionRegistration>!<FaceRecognitionExtraction>+<FaceRecognitionEmbedding>+<FaceRecognitionQuantization>:MatchProbability(ByteL1)"); |
| 35 | - Globals->abbreviations.insert("FaceRecognitionNoTraining", "FaceDetection!ASEFEyes+Affine(86,86,0.25,0.35)!Blur(1.1)+Gamma(0.2)+DoG(1,2)+ContrastEq(0.1,10)+Mask+LBP(1,2)+RectRegions(8,8,6,6)+Hist(59)+Cat:ChiSquared"); | |
| 36 | 35 | Globals->abbreviations.insert("GenderClassification", "FaceDetection!<FaceClassificationRegistration>!<FaceClassificationExtraction>+<GenderClassifier>+Discard"); |
| 37 | 36 | Globals->abbreviations.insert("AgeRegression", "FaceDetection!<FaceClassificationRegistration>!<FaceClassificationExtraction>+<AgeRegressor>+Discard"); |
| 38 | 37 | Globals->abbreviations.insert("FaceQuality", "Open!Cascade(FrontalFace)+ASEFEyes+Affine(64,64,0.25,0.35)+ImageQuality+Cvt(Gray)+DFFS+Discard"); |
| ... | ... | @@ -43,6 +42,7 @@ class AlgorithmsInitializer : public Initializer |
| 43 | 42 | Globals->abbreviations.insert("OpenBR", "FaceRecognition"); |
| 44 | 43 | Globals->abbreviations.insert("GenderEstimation", "GenderClassification"); |
| 45 | 44 | Globals->abbreviations.insert("AgeEstimation", "AgeRegression"); |
| 45 | + Globals->abbreviations.insert("FaceRecognitionHoG", "Open+Cvt(Gray)+Cascade(FrontalFace)+ASEFEyes+Affine(64,64,0.25,0.35)+Gradient+Bin(0,360,8,true)+Merge+Integral+IntegralSampler+RootNorm+ProductQuantization(2,true):ProductQuantization(true)"); | |
| 46 | 46 | |
| 47 | 47 | // Generic Image Processing |
| 48 | 48 | Globals->abbreviations.insert("SIFT", "Open+KeyPointDetector(SIFT)+KeyPointDescriptor(SIFT):KeyPointMatcher(BruteForce)"); |
| ... | ... | @@ -50,7 +50,7 @@ class AlgorithmsInitializer : public Initializer |
| 50 | 50 | Globals->abbreviations.insert("SmallSIFT", "Open+LimitSize(512)+KeyPointDetector(SIFT)+KeyPointDescriptor(SIFT):KeyPointMatcher(BruteForce)"); |
| 51 | 51 | Globals->abbreviations.insert("SmallSURF", "Open+LimitSize(512)+KeyPointDetector(SURF)+KeyPointDescriptor(SURF):KeyPointMatcher(BruteForce)"); |
| 52 | 52 | Globals->abbreviations.insert("ColorHist", "Open+LimitSize(512)!EnsureChannels(3)+SplitChannels+Hist(256,0,8)+Cat+Normalize(L1):L2"); |
| 53 | - Globals->abbreviations.insert("IHH", "Open+(RG+MAdd(0.5))/(Cvt(Gray)+Gradient+Bin(0,360,8,true))+Merge+Integral+IntegralSampler+CvtFloat+WordWise(RowWisePCA(8)+RowWiseMeanCenter+Binarize,Identity):L2"); | |
| 53 | + Globals->abbreviations.insert("ImageRetrieval", "Open+Cvt(Gray)+Cascade(FrontalFace)+ASEFEyes+Affine(88,88,0.25,0.35)+Gradient+Bin(0,360,8,true)+Merge+Integral+IntegralSampler+WordWise(RowWisePCA(8)+RowWiseMeanCenter+Binarize,RowWisePCA)+Sentence:SentenceSimilarity"); | |
| 54 | 54 | |
| 55 | 55 | // Hash |
| 56 | 56 | Globals->abbreviations.insert("FileName", "Name+Identity:Identical"); | ... | ... |
sdk/plugins/cvt.cpp
| ... | ... | @@ -181,12 +181,17 @@ class RGTransform : public UntrainableTransform |
| 181 | 181 | for (int i=0; i<m.rows; i++) |
| 182 | 182 | for (int j=0; j<m.cols; j++) { |
| 183 | 183 | Vec3b v = m.at<Vec3b>(i,j); |
| 184 | - uchar& b = v[0]; | |
| 185 | - uchar& g = v[1]; | |
| 186 | - uchar& r = v[2]; | |
| 187 | - | |
| 188 | - R.at<uchar>(i, j) = saturate_cast<uchar>(255.0*r/(r+g+b)); | |
| 189 | - G.at<uchar>(i, j) = saturate_cast<uchar>(255.0*g/(r+g+b)); | |
| 184 | + const int b = v[0]; | |
| 185 | + const int g = v[1]; | |
| 186 | + const int r = v[2]; | |
| 187 | + const int sum = b + g + r; | |
| 188 | + if (sum > 0) { | |
| 189 | + R.at<uchar>(i, j) = saturate_cast<uchar>(255.0*r/(r+g+b)); | |
| 190 | + G.at<uchar>(i, j) = saturate_cast<uchar>(255.0*g/(r+g+b)); | |
| 191 | + } else { | |
| 192 | + R.at<uchar>(i, j) = 0; | |
| 193 | + G.at<uchar>(i, j) = 0; | |
| 194 | + } | |
| 190 | 195 | } |
| 191 | 196 | |
| 192 | 197 | dst.append(R); | ... | ... |
sdk/plugins/distance.cpp
| ... | ... | @@ -14,11 +14,13 @@ |
| 14 | 14 | * limitations under the License. * |
| 15 | 15 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ |
| 16 | 16 | |
| 17 | +#include <QFutureSynchronizer> | |
| 17 | 18 | #include <QtConcurrentRun> |
| 18 | 19 | #include <opencv2/imgproc/imgproc.hpp> |
| 19 | 20 | #include <openbr_plugin.h> |
| 20 | 21 | |
| 21 | 22 | #include "core/distance_sse.h" |
| 23 | +#include "core/qtutils.h" | |
| 22 | 24 | |
| 23 | 25 | using namespace cv; |
| 24 | 26 | |
| ... | ... | @@ -154,11 +156,11 @@ class PipeDistance : public Distance |
| 154 | 156 | |
| 155 | 157 | void train(const TemplateList &data) |
| 156 | 158 | { |
| 157 | - QList< QFuture<void> > futures; | |
| 159 | + QFutureSynchronizer<void> futures; | |
| 158 | 160 | foreach (br::Distance *distance, distances) |
| 159 | - if (Globals->parallelism) futures.append(QtConcurrent::run(distance, &Distance::train, data)); | |
| 160 | - else distance->train(data); | |
| 161 | - Globals->trackFutures(futures); | |
| 161 | + if (Globals->parallelism) futures.addFuture(QtConcurrent::run(distance, &Distance::train, data)); | |
| 162 | + else distance->train(data); | |
| 163 | + futures.waitForFinished(); | |
| 162 | 164 | } |
| 163 | 165 | |
| 164 | 166 | float compare(const Template &a, const Template &b) const | ... | ... |
sdk/plugins/eigen3.cpp
| ... | ... | @@ -145,15 +145,15 @@ protected: |
| 145 | 145 | |
| 146 | 146 | if (keep < 1) { |
| 147 | 147 | // Keep eigenvectors that retain a certain energy percentage. |
| 148 | - double totalEnergy = allEVals.sum(); | |
| 148 | + const double totalEnergy = allEVals.sum(); | |
| 149 | 149 | if (totalEnergy == 0) { |
| 150 | 150 | keep = 0; |
| 151 | 151 | } else { |
| 152 | 152 | double currentEnergy = 0; |
| 153 | - int i; | |
| 154 | - for (i=1; i<=allEVals.rows(); i++) { | |
| 155 | - currentEnergy += allEVals(allEVals.rows()-i); | |
| 156 | - if (currentEnergy / totalEnergy >= keep) break; | |
| 153 | + int i=0; | |
| 154 | + while ((currentEnergy / totalEnergy < keep) && (i < allEVals.rows())) { | |
| 155 | + currentEnergy += allEVals(allEVals.rows()-(i+1)); | |
| 156 | + i++; | |
| 157 | 157 | } |
| 158 | 158 | keep = i - drop; |
| 159 | 159 | } | ... | ... |
sdk/plugins/format.cpp
| ... | ... | @@ -242,12 +242,11 @@ class DefaultFormat : public Format |
| 242 | 242 | |
| 243 | 243 | void write(const Template &t) const |
| 244 | 244 | { |
| 245 | - if (t.size() != 1) { | |
| 245 | + if (t.size() > 1) { | |
| 246 | 246 | videoFormat videoWriter; |
| 247 | 247 | videoWriter.file = file; |
| 248 | 248 | videoWriter.write(t); |
| 249 | - } | |
| 250 | - else { | |
| 249 | + } else if (t.size() == 1) { | |
| 251 | 250 | imwrite(file.name.toStdString(), t); |
| 252 | 251 | } |
| 253 | 252 | } |
| ... | ... | @@ -565,7 +564,6 @@ class webcamFormat : public Format |
| 565 | 564 | |
| 566 | 565 | BR_REGISTER(Format, webcamFormat) |
| 567 | 566 | |
| 568 | -#ifndef BR_EMBEDDED | |
| 569 | 567 | /*! |
| 570 | 568 | * \ingroup formats |
| 571 | 569 | * \brief Decodes images from Base64 xml |
| ... | ... | @@ -578,13 +576,15 @@ class xmlFormat : public Format |
| 578 | 576 | |
| 579 | 577 | Template read() const |
| 580 | 578 | { |
| 579 | + Template t; | |
| 580 | + | |
| 581 | +#ifndef BR_EMBEDDED | |
| 581 | 582 | QDomDocument doc(file); |
| 582 | 583 | QFile f(file); |
| 583 | 584 | if (!f.open(QIODevice::ReadOnly)) qFatal("Unable to open %s for reading.", qPrintable(file.flat())); |
| 584 | 585 | if (!doc.setContent(&f)) qFatal("Unable to parse %s.", qPrintable(file.flat())); |
| 585 | 586 | f.close(); |
| 586 | 587 | |
| 587 | - Template t; | |
| 588 | 588 | QDomElement docElem = doc.documentElement(); |
| 589 | 589 | QDomNode subject = docElem.firstChild(); |
| 590 | 590 | while (!subject.isNull()) { |
| ... | ... | @@ -620,6 +620,7 @@ class xmlFormat : public Format |
| 620 | 620 | if (current.month() < dob.month()) age--; |
| 621 | 621 | t.file.set("Age", age); |
| 622 | 622 | } |
| 623 | +#endif // BR_EMBEDDED | |
| 623 | 624 | |
| 624 | 625 | return t; |
| 625 | 626 | } |
| ... | ... | @@ -632,7 +633,6 @@ class xmlFormat : public Format |
| 632 | 633 | }; |
| 633 | 634 | |
| 634 | 635 | BR_REGISTER(Format, xmlFormat) |
| 635 | -#endif // BR_EMBEDDED | |
| 636 | 636 | |
| 637 | 637 | } // namespace br |
| 638 | 638 | ... | ... |
sdk/plugins/gallery.cpp
| ... | ... | @@ -14,6 +14,7 @@ |
| 14 | 14 | * limitations under the License. * |
| 15 | 15 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ |
| 16 | 16 | |
| 17 | +#include <QtConcurrentRun> | |
| 17 | 18 | #ifndef BR_EMBEDDED |
| 18 | 19 | #include <QNetworkAccessManager> |
| 19 | 20 | #include <QNetworkReply> |
| ... | ... | @@ -103,9 +104,14 @@ class EmptyGallery : public Gallery |
| 103 | 104 | |
| 104 | 105 | // Add immediate subfolders |
| 105 | 106 | QDir dir(file); |
| 106 | - foreach (const QString &folder, NaturalStringSort(dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot))) | |
| 107 | - foreach (const QString &file, QtUtils::getFiles(dir.absoluteFilePath(folder), true)) | |
| 108 | - templates.append(File(file, folder)); | |
| 107 | + QList< QFuture<TemplateList> > futures; | |
| 108 | + foreach (const QString &folder, NaturalStringSort(dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot))) { | |
| 109 | + const QDir subdir = dir.absoluteFilePath(folder); | |
| 110 | + if (Globals->parallelism) futures.append(QtConcurrent::run(&EmptyGallery::getTemplates, subdir)); | |
| 111 | + else templates.append(getTemplates(subdir)); | |
| 112 | + } | |
| 113 | + foreach (const QFuture<TemplateList> &future, futures) | |
| 114 | + templates.append(future.result()); | |
| 109 | 115 | |
| 110 | 116 | // Add root folder |
| 111 | 117 | foreach (const QString &fileName, QtUtils::getFiles(file.name, false)) |
| ... | ... | @@ -130,6 +136,15 @@ class EmptyGallery : public Gallery |
| 130 | 136 | format->write(t); |
| 131 | 137 | } |
| 132 | 138 | } |
| 139 | + | |
| 140 | + static TemplateList getTemplates(const QDir &dir) | |
| 141 | + { | |
| 142 | + const QStringList files = QtUtils::getFiles(dir, true); | |
| 143 | + TemplateList templates; templates.reserve(files.size()); | |
| 144 | + foreach (const QString &file, files) | |
| 145 | + templates.append(File(file, dir.dirName())); | |
| 146 | + return templates; | |
| 147 | + } | |
| 133 | 148 | }; |
| 134 | 149 | |
| 135 | 150 | BR_REGISTER(Gallery, EmptyGallery) |
| ... | ... | @@ -662,7 +677,7 @@ class googleGallery : public Gallery |
| 662 | 677 | QNetworkReply *reply = networkAccessManager.get(request); |
| 663 | 678 | |
| 664 | 679 | while (!reply->isFinished()) |
| 665 | - QCoreApplication::processEvents(); | |
| 680 | + QThread::yieldCurrentThread(); | |
| 666 | 681 | |
| 667 | 682 | QString data(reply->readAll()); |
| 668 | 683 | delete reply; | ... | ... |
sdk/plugins/gui.cmake
0 โ 100644
sdk/plugins/gui.cpp
0 โ 100644
| 1 | +#include <QApplication> | |
| 2 | +#include <QLabel> | |
| 3 | +#include <opencv2/imgproc/imgproc.hpp> | |
| 4 | +#include <openbr_plugin.h> | |
| 5 | + | |
| 6 | +using namespace cv; | |
| 7 | + | |
| 8 | +namespace br | |
| 9 | +{ | |
| 10 | +QImage toQImage(const Mat &mat) | |
| 11 | +{ | |
| 12 | + // Convert to 8U depth | |
| 13 | + Mat mat8u; | |
| 14 | + if (mat.depth() != CV_8U) { | |
| 15 | + double globalMin = std::numeric_limits<double>::max(); | |
| 16 | + double globalMax = -std::numeric_limits<double>::max(); | |
| 17 | + | |
| 18 | + std::vector<Mat> mv; | |
| 19 | + split(mat, mv); | |
| 20 | + for (size_t i=0; i<mv.size(); i++) { | |
| 21 | + double min, max; | |
| 22 | + minMaxLoc(mv[i], &min, &max); | |
| 23 | + globalMin = std::min(globalMin, min); | |
| 24 | + globalMax = std::max(globalMax, max); | |
| 25 | + } | |
| 26 | + assert(globalMax >= globalMin); | |
| 27 | + | |
| 28 | + double range = globalMax - globalMin; | |
| 29 | + if (range != 0) { | |
| 30 | + double scale = 255 / range; | |
| 31 | + convertScaleAbs(mat, mat8u, scale, -(globalMin * scale)); | |
| 32 | + } else { | |
| 33 | + // Monochromatic | |
| 34 | + mat8u = Mat(mat.size(), CV_8UC1, Scalar((globalMin+globalMax)/2)); | |
| 35 | + } | |
| 36 | + } else { | |
| 37 | + mat8u = mat; | |
| 38 | + } | |
| 39 | + | |
| 40 | + // Convert to 3 channels | |
| 41 | + Mat mat8uc3; | |
| 42 | + if (mat8u.channels() == 4) cvtColor(mat8u, mat8uc3, CV_BGRA2RGB); | |
| 43 | + else if (mat8u.channels() == 3) cvtColor(mat8u, mat8uc3, CV_BGR2RGB); | |
| 44 | + else if (mat8u.channels() == 1) cvtColor(mat8u, mat8uc3, CV_GRAY2RGB); | |
| 45 | + | |
| 46 | + return QImage(mat8uc3.data, mat8uc3.cols, mat8uc3.rows, 3*mat8uc3.cols, QImage::Format_RGB888).copy(); | |
| 47 | +} | |
| 48 | + | |
| 49 | +// Provides slots for manipulating a QLabel, but does not inherit from QWidget. | |
| 50 | +// Therefore, it can be moved to the main thread if not created there initially | |
| 51 | +// since god forbid you create a QWidget subclass in not the main thread. | |
| 52 | +class GUIProxy : public QObject | |
| 53 | +{ | |
| 54 | + Q_OBJECT | |
| 55 | +public: | |
| 56 | + QLabel * window; | |
| 57 | + | |
| 58 | + GUIProxy() | |
| 59 | + { | |
| 60 | + window = NULL; | |
| 61 | + } | |
| 62 | + | |
| 63 | +public slots: | |
| 64 | + | |
| 65 | + void showImage(const QPixmap & input) | |
| 66 | + { | |
| 67 | + window->setPixmap(input); | |
| 68 | + window->setFixedSize(input.size()); | |
| 69 | + window->setVisible(true); | |
| 70 | + } | |
| 71 | + | |
| 72 | + void createWindow() | |
| 73 | + { | |
| 74 | + delete window; | |
| 75 | + window = new QLabel(); | |
| 76 | + } | |
| 77 | +}; | |
| 78 | + | |
| 79 | +/*! | |
| 80 | + * \ingroup transforms | |
| 81 | + * \brief Displays templates in a GUI pop-up window using QT. | |
| 82 | + * \author Charles Otto \cite caotto | |
| 83 | + * Unlike ShowTransform, this can be used with parallelism enabled, although it | |
| 84 | + * is considered TimeVarying. | |
| 85 | + */ | |
| 86 | +class Show2Transform : public TimeVaryingTransform | |
| 87 | +{ | |
| 88 | + Q_OBJECT | |
| 89 | +public: | |
| 90 | + Show2Transform() : TimeVaryingTransform(false, false) | |
| 91 | + { | |
| 92 | + // Create our GUI proxy | |
| 93 | + gui = new GUIProxy(); | |
| 94 | + // Move it to the main thread, this means signals we send to it will | |
| 95 | + // be run in the main thread, which is hopefully in an event loop | |
| 96 | + gui->moveToThread(QApplication::instance()->thread()); | |
| 97 | + // Connect our signals to the proxy's slots | |
| 98 | + connect(this, SIGNAL(needWindow()), gui, SLOT(createWindow()), Qt::BlockingQueuedConnection); | |
| 99 | + connect(this, SIGNAL(updateImage(QPixmap)), gui,SLOT(showImage(QPixmap))); | |
| 100 | + } | |
| 101 | + | |
| 102 | + ~Show2Transform() | |
| 103 | + { | |
| 104 | + delete gui; | |
| 105 | + } | |
| 106 | + | |
| 107 | + void train(const TemplateList &data) { (void) data; } | |
| 108 | + | |
| 109 | + void project(const TemplateList &src, TemplateList &dst) const | |
| 110 | + { | |
| 111 | + Transform * non_const = (Show2Transform *) this; | |
| 112 | + non_const->projectUpdate(src,dst); | |
| 113 | + } | |
| 114 | + | |
| 115 | + void projectUpdate(const TemplateList &src, TemplateList &dst) | |
| 116 | + { | |
| 117 | + dst = src; | |
| 118 | + | |
| 119 | + if (src.empty()) | |
| 120 | + return; | |
| 121 | + | |
| 122 | + foreach (const Template & t, src) { | |
| 123 | + foreach(const cv::Mat & m, t) { | |
| 124 | + qImageBuffer = toQImage(m); | |
| 125 | + displayBuffer.convertFromImage(qImageBuffer); | |
| 126 | + // Emit an explicit copy of our pixmap so that the pixmap used | |
| 127 | + // by the main thread isn't damaged when we update displayBuffer | |
| 128 | + // later. | |
| 129 | + emit updateImage(displayBuffer.copy(displayBuffer.rect())); | |
| 130 | + } | |
| 131 | + } | |
| 132 | + } | |
| 133 | + | |
| 134 | + void finalize(TemplateList & output) | |
| 135 | + { | |
| 136 | + (void) output; | |
| 137 | + // todo: hide window? | |
| 138 | + } | |
| 139 | + | |
| 140 | + void init() | |
| 141 | + { | |
| 142 | + emit needWindow(); | |
| 143 | + } | |
| 144 | + | |
| 145 | +protected: | |
| 146 | + GUIProxy * gui; | |
| 147 | + QImage qImageBuffer; | |
| 148 | + QPixmap displayBuffer; | |
| 149 | + | |
| 150 | +signals: | |
| 151 | + void needWindow(); | |
| 152 | + void updateImage(const QPixmap & input); | |
| 153 | +}; | |
| 154 | + | |
| 155 | +BR_REGISTER(Transform, Show2Transform) | |
| 156 | + | |
| 157 | +} // namespace br | |
| 158 | + | |
| 159 | +#include "gui.moc" | ... | ... |
sdk/plugins/hist.cpp
| ... | ... | @@ -111,7 +111,7 @@ class RankTransform : public UntrainableTransform |
| 111 | 111 | assert(m.channels() == 1); |
| 112 | 112 | dst = Mat(m.rows, m.cols, CV_32FC1); |
| 113 | 113 | typedef QPair<float,int> Tuple; |
| 114 | - QList<Tuple> tuples = Common::Sort(OpenCVUtils::matrixToVector(m)); | |
| 114 | + QList<Tuple> tuples = Common::Sort(OpenCVUtils::matrixToVector<float>(m)); | |
| 115 | 115 | |
| 116 | 116 | float prevValue = 0; |
| 117 | 117 | int prevRank = 0; | ... | ... |
sdk/plugins/integral.cpp
| ... | ... | @@ -28,7 +28,7 @@ BR_REGISTER(Transform, IntegralTransform) |
| 28 | 28 | |
| 29 | 29 | /*! |
| 30 | 30 | * \ingroup transforms |
| 31 | - * \brief Sliding window feature extraction from a multi-channel intergral image. | |
| 31 | + * \brief Sliding window feature extraction from a multi-channel integral image. | |
| 32 | 32 | * \author Josh Klontz \cite jklontz |
| 33 | 33 | */ |
| 34 | 34 | class IntegralSamplerTransform : public UntrainableTransform |
| ... | ... | @@ -38,9 +38,9 @@ class IntegralSamplerTransform : public UntrainableTransform |
| 38 | 38 | Q_PROPERTY(float scaleFactor READ get_scaleFactor WRITE set_scaleFactor RESET reset_scaleFactor STORED false) |
| 39 | 39 | Q_PROPERTY(float stepFactor READ get_stepFactor WRITE set_stepFactor RESET reset_stepFactor STORED false) |
| 40 | 40 | Q_PROPERTY(int minSize READ get_minSize WRITE set_minSize RESET reset_minSize STORED false) |
| 41 | - BR_PROPERTY(int, scales, 4) | |
| 41 | + BR_PROPERTY(int, scales, 5) | |
| 42 | 42 | BR_PROPERTY(float, scaleFactor, 1.5) |
| 43 | - BR_PROPERTY(float, stepFactor, 0.25) | |
| 43 | + BR_PROPERTY(float, stepFactor, 0.75) | |
| 44 | 44 | BR_PROPERTY(int, minSize, 8) |
| 45 | 45 | |
| 46 | 46 | void project(const Template &src, Template &dst) const |
| ... | ... | @@ -53,22 +53,23 @@ class IntegralSamplerTransform : public UntrainableTransform |
| 53 | 53 | const int rowStep = channels * m.cols; |
| 54 | 54 | |
| 55 | 55 | int descriptors = 0; |
| 56 | - int currentSize = min(m.rows, m.cols)-1; | |
| 56 | + float idealSize = min(m.rows, m.cols)-1; | |
| 57 | 57 | for (int scale=0; scale<scales; scale++) { |
| 58 | - descriptors += int(1+(m.rows-currentSize)/(currentSize*stepFactor)) * | |
| 59 | - int(1+(m.cols-currentSize)/(currentSize*stepFactor)); | |
| 60 | - currentSize /= scaleFactor; | |
| 61 | - if (currentSize < minSize) | |
| 62 | - break; | |
| 58 | + const int currentSize(idealSize); | |
| 59 | + descriptors += (1+(m.rows-currentSize-1)/int(idealSize*stepFactor)) * | |
| 60 | + (1+(m.cols-currentSize-1)/int(idealSize*stepFactor)); | |
| 61 | + idealSize /= scaleFactor; | |
| 62 | + if (idealSize < minSize) break; | |
| 63 | 63 | } |
| 64 | 64 | Mat n(descriptors, channels, CV_32FC1); |
| 65 | 65 | |
| 66 | 66 | const qint32 *dataIn = (qint32*)m.data; |
| 67 | 67 | float *dataOut = (float*)n.data; |
| 68 | - currentSize = min(m.rows, m.cols)-1; | |
| 68 | + idealSize = min(m.rows, m.cols)-1; | |
| 69 | 69 | int index = 0; |
| 70 | 70 | for (int scale=0; scale<scales; scale++) { |
| 71 | - const int currentStep = currentSize * stepFactor; | |
| 71 | + const int currentSize(idealSize); | |
| 72 | + const int currentStep(idealSize*stepFactor); | |
| 72 | 73 | for (int i=currentSize; i<m.rows; i+=currentStep) { |
| 73 | 74 | for (int j=currentSize; j<m.cols; j+=currentStep) { |
| 74 | 75 | InputDescriptor a(dataIn+((i-currentSize)*rowStep+(j-currentSize)*channels), channels, 1); |
| ... | ... | @@ -80,10 +81,13 @@ class IntegralSamplerTransform : public UntrainableTransform |
| 80 | 81 | index++; |
| 81 | 82 | } |
| 82 | 83 | } |
| 83 | - currentSize /= scaleFactor; | |
| 84 | - if (currentSize < minSize) | |
| 85 | - break; | |
| 84 | + idealSize /= scaleFactor; | |
| 85 | + if (idealSize < minSize) break; | |
| 86 | 86 | } |
| 87 | + | |
| 88 | + if (descriptors != index) | |
| 89 | + qFatal("Allocated %d descriptors but computed %d.", descriptors, index); | |
| 90 | + | |
| 87 | 91 | dst.m() = n; |
| 88 | 92 | } |
| 89 | 93 | }; |
| ... | ... | @@ -133,38 +137,51 @@ class WordWiseTransform : public Transform |
| 133 | 137 | Q_OBJECT |
| 134 | 138 | Q_PROPERTY(br::Transform* getWords READ get_getWords WRITE set_getWords RESET reset_getWords) |
| 135 | 139 | Q_PROPERTY(br::Transform* byWord READ get_byWord WRITE set_byWord RESET reset_byWord) |
| 140 | + Q_PROPERTY(int numWords READ get_numWords WRITE set_numWords RESET reset_numWords) | |
| 136 | 141 | BR_PROPERTY(br::Transform*, getWords, NULL) |
| 137 | 142 | BR_PROPERTY(br::Transform*, byWord, NULL) |
| 143 | + BR_PROPERTY(int, numWords, 0) | |
| 138 | 144 | |
| 139 | 145 | void train(const TemplateList &data) |
| 140 | 146 | { |
| 141 | 147 | getWords->train(data); |
| 142 | - TemplateList words; | |
| 143 | - getWords->project(data, words); | |
| 148 | + TemplateList bins; | |
| 149 | + getWords->project(data, bins); | |
| 144 | 150 | |
| 145 | - const int columns = data.first().m().cols; | |
| 146 | - int numWords = 0; | |
| 147 | - foreach (const Template &t, words) { | |
| 151 | + numWords = 0; | |
| 152 | + foreach (const Template &t, bins) { | |
| 148 | 153 | double minVal, maxVal; |
| 149 | 154 | minMaxLoc(t, &minVal, &maxVal); |
| 150 | 155 | numWords = max(numWords, int(maxVal)+1); |
| 151 | 156 | } |
| 152 | 157 | |
| 153 | - QVector<int> wordCounts(numWords, 0); | |
| 154 | - foreach (const Template &t, words) { | |
| 155 | - const Mat &m = t.m(); | |
| 156 | - for (int i=0; i<m.rows; i++) | |
| 157 | - wordCounts[m.at<uchar>(i,0)]++; | |
| 158 | - } | |
| 159 | - | |
| 160 | - QVector<Mat> trainingWords(numWords); | |
| 161 | - for (int i=0; i<numWords; i++) | |
| 162 | - trainingWords[i] = Mat(wordCounts[i], columns, CV_8UC1); | |
| 158 | + TemplateList reworded; reworded.reserve(data.size()); | |
| 159 | + foreach (const Template &t, data) | |
| 160 | + reworded.append(reword(t)); | |
| 161 | + byWord->train(reworded); | |
| 163 | 162 | } |
| 164 | 163 | |
| 165 | 164 | void project(const Template &src, Template &dst) const |
| 166 | 165 | { |
| 167 | - (void) src; (void) dst; | |
| 166 | + byWord->project(reword(src), dst); | |
| 167 | + } | |
| 168 | + | |
| 169 | + Template reword(const Template &src) const | |
| 170 | + { | |
| 171 | + Template words; | |
| 172 | + getWords->project(src, words); | |
| 173 | + QVector<int> wordCounts(numWords, 0); | |
| 174 | + for (int i=0; i<words.m().rows; i++) | |
| 175 | + wordCounts[words.m().at<uchar>(i,0)]++; | |
| 176 | + Template reworded(src.file); reworded.reserve(numWords); | |
| 177 | + for (int i=0; i<numWords; i++) | |
| 178 | + reworded.append(Mat(wordCounts[i], src.m().cols, src.m().type())); | |
| 179 | + QVector<int> indicies(numWords, 0); | |
| 180 | + for (int i=0; i<src.m().rows; i++) { | |
| 181 | + const int word = words.m().at<uchar>(i,0); | |
| 182 | + src.m().row(i).copyTo(reworded[word].row(indicies[word]++)); | |
| 183 | + } | |
| 184 | + return reworded; | |
| 168 | 185 | } |
| 169 | 186 | }; |
| 170 | 187 | ... | ... |
sdk/plugins/meta.cpp
| ... | ... | @@ -14,6 +14,7 @@ |
| 14 | 14 | * limitations under the License. * |
| 15 | 15 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ |
| 16 | 16 | |
| 17 | +#include <QFutureSynchronizer> | |
| 17 | 18 | #include <QtConcurrentRun> |
| 18 | 19 | #include <openbr_plugin.h> |
| 19 | 20 | |
| ... | ... | @@ -60,76 +61,6 @@ static void _train(Transform *transform, const TemplateList *data) |
| 60 | 61 | transform->train(*data); |
| 61 | 62 | } |
| 62 | 63 | |
| 63 | -// For handling progress feedback | |
| 64 | -static int depth = 0; | |
| 65 | - | |
| 66 | -static void acquireStep() | |
| 67 | -{ | |
| 68 | - if (depth == 0) { | |
| 69 | - Globals->currentStep = 0; | |
| 70 | - Globals->totalSteps = 1; | |
| 71 | - } | |
| 72 | - depth++; | |
| 73 | -} | |
| 74 | - | |
| 75 | -static void releaseStep() | |
| 76 | -{ | |
| 77 | - depth--; | |
| 78 | - Globals->currentStep = floor(Globals->currentStep * pow(10.0, double(depth))) / pow(10.0, double(depth)); | |
| 79 | - if (depth == 0) | |
| 80 | - Globals->totalSteps = 0; | |
| 81 | -} | |
| 82 | - | |
| 83 | -static void incrementStep() | |
| 84 | -{ | |
| 85 | - Globals->currentStep += 1.0 / pow(10.0, double(depth)); | |
| 86 | -} | |
| 87 | - | |
| 88 | -/*! | |
| 89 | - * \brief A MetaTransform that aggregates some sub-transforms | |
| 90 | - */ | |
| 91 | -class BR_EXPORT CompositeTransform : public TimeVaryingTransform | |
| 92 | -{ | |
| 93 | - Q_OBJECT | |
| 94 | - | |
| 95 | -public: | |
| 96 | - Q_PROPERTY(QList<br::Transform*> transforms READ get_transforms WRITE set_transforms RESET reset_transforms) | |
| 97 | - BR_PROPERTY(QList<br::Transform*>, transforms, QList<br::Transform*>()) | |
| 98 | - | |
| 99 | - virtual void project(const Template &src, Template &dst) const | |
| 100 | - { | |
| 101 | - if (timeVarying()) qFatal("No const project defined for time-varying transform"); | |
| 102 | - _project(src, dst); | |
| 103 | - } | |
| 104 | - | |
| 105 | - virtual void project(const TemplateList &src, TemplateList &dst) const | |
| 106 | - { | |
| 107 | - if (timeVarying()) qFatal("No const project defined for time-varying transform"); | |
| 108 | - _project(src, dst); | |
| 109 | - } | |
| 110 | - | |
| 111 | - bool timeVarying() const { return isTimeVarying; } | |
| 112 | - | |
| 113 | - void init() | |
| 114 | - { | |
| 115 | - isTimeVarying = false; | |
| 116 | - foreach (const br::Transform *transform, transforms) { | |
| 117 | - if (transform->timeVarying()) { | |
| 118 | - isTimeVarying = true; | |
| 119 | - break; | |
| 120 | - } | |
| 121 | - } | |
| 122 | - } | |
| 123 | - | |
| 124 | -protected: | |
| 125 | - bool isTimeVarying; | |
| 126 | - | |
| 127 | - virtual void _project(const Template & src, Template & dst) const = 0; | |
| 128 | - virtual void _project(const TemplateList & src, TemplateList & dst) const = 0; | |
| 129 | - | |
| 130 | - CompositeTransform() : TimeVaryingTransform(false) {} | |
| 131 | -}; | |
| 132 | - | |
| 133 | 64 | /*! |
| 134 | 65 | * \ingroup Transforms |
| 135 | 66 | * \brief Transforms in series. |
| ... | ... | @@ -146,16 +77,13 @@ class PipeTransform : public CompositeTransform |
| 146 | 77 | |
| 147 | 78 | void train(const TemplateList &data) |
| 148 | 79 | { |
| 149 | - acquireStep(); | |
| 150 | - | |
| 151 | 80 | TemplateList copy(data); |
| 152 | 81 | for (int i=0; i<transforms.size(); i++) { |
| 82 | + fprintf(stderr, "%s training... ", qPrintable(transforms[i]->objectName())); | |
| 153 | 83 | transforms[i]->train(copy); |
| 84 | + fprintf(stderr, "projecting...\n"); | |
| 154 | 85 | copy >> *transforms[i]; |
| 155 | - incrementStep(); | |
| 156 | 86 | } |
| 157 | - | |
| 158 | - releaseStep(); | |
| 159 | 87 | } |
| 160 | 88 | |
| 161 | 89 | void backProject(const Template &dst, Template &src) const |
| ... | ... | @@ -284,6 +212,38 @@ BR_REGISTER(Transform, ExpandTransform) |
| 284 | 212 | |
| 285 | 213 | /*! |
| 286 | 214 | * \ingroup transforms |
| 215 | + * \brief It's like the opposite of ExpandTransform, but not really | |
| 216 | + * \author Charles Otto \cite caotto | |
| 217 | + * | |
| 218 | + * Given a set of templatelists as input, concatenate them onto a single Template | |
| 219 | + */ | |
| 220 | +class ContractTransform : public UntrainableMetaTransform | |
| 221 | +{ | |
| 222 | + Q_OBJECT | |
| 223 | + | |
| 224 | + virtual void project(const TemplateList &src, TemplateList &dst) const | |
| 225 | + { | |
| 226 | + //dst = Expanded(src); | |
| 227 | + Template out; | |
| 228 | + | |
| 229 | + foreach (const Template & t, src) { | |
| 230 | + out.merge(t); | |
| 231 | + } | |
| 232 | + dst.clear(); | |
| 233 | + dst.append(out); | |
| 234 | + } | |
| 235 | + | |
| 236 | + virtual void project(const Template & src, Template & dst) const | |
| 237 | + { | |
| 238 | + qFatal("this has gone bad"); | |
| 239 | + (void) src; (void) dst; | |
| 240 | + } | |
| 241 | +}; | |
| 242 | + | |
| 243 | +BR_REGISTER(Transform, ContractTransform) | |
| 244 | + | |
| 245 | +/*! | |
| 246 | + * \ingroup transforms | |
| 287 | 247 | * \brief Transforms in parallel. |
| 288 | 248 | * \author Josh Klontz \cite jklontz |
| 289 | 249 | * |
| ... | ... | @@ -297,13 +257,12 @@ class ForkTransform : public CompositeTransform |
| 297 | 257 | |
| 298 | 258 | void train(const TemplateList &data) |
| 299 | 259 | { |
| 300 | - QList< QFuture<void> > futures; | |
| 301 | - const bool threaded = Globals->parallelism && (transforms.size() > 1); | |
| 260 | + QFutureSynchronizer<void> futures; | |
| 302 | 261 | for (int i=0; i<transforms.size(); i++) { |
| 303 | - if (threaded) futures.append(QtConcurrent::run(_train, transforms[i], &data)); | |
| 304 | - else _train (transforms[i], &data); | |
| 262 | + if (Globals->parallelism) futures.addFuture(QtConcurrent::run(_train, transforms[i], &data)); | |
| 263 | + else _train (transforms[i], &data); | |
| 305 | 264 | } |
| 306 | - if (threaded) Globals->trackFutures(futures); | |
| 265 | + futures.waitForFinished(); | |
| 307 | 266 | } |
| 308 | 267 | |
| 309 | 268 | void backProject(const Template &dst, Template &src) const {Transform::backProject(dst, src);} |
| ... | ... | @@ -626,6 +585,15 @@ public: |
| 626 | 585 | // Process the single elemnt templates in parallel if parallelism is enabled. |
| 627 | 586 | void project(const TemplateList &src, TemplateList &dst) const |
| 628 | 587 | { |
| 588 | + // Little ugly, but if we own a timeVaryingTransform and this gets called | |
| 589 | + // cast off the const modifier and use projectUpdate. This allows us to | |
| 590 | + // act as a single point of entry. | |
| 591 | + if (transform->timeVarying()) | |
| 592 | + { | |
| 593 | + DistributeTemplateTransform * non_const = (DistributeTemplateTransform *) this; | |
| 594 | + non_const->projectUpdate(src,dst); | |
| 595 | + return; | |
| 596 | + } | |
| 629 | 597 | // Pre-allocate output for each template |
| 630 | 598 | QList<TemplateList> output_buffer; |
| 631 | 599 | output_buffer.reserve(src.size()); |
| ... | ... | @@ -639,22 +607,26 @@ public: |
| 639 | 607 | output_buffer.append(TemplateList()); |
| 640 | 608 | } |
| 641 | 609 | |
| 642 | - QList< QFuture<void> > futures; | |
| 643 | - futures.reserve(src.size()); | |
| 610 | + QFutureSynchronizer<void> futures; | |
| 644 | 611 | for (int i=0; i<src.size(); i++) { |
| 645 | 612 | input_buffer[i].append(src[i]); |
| 646 | - if (Globals->parallelism) | |
| 647 | - futures.append(QtConcurrent::run(_projectList, transform, &input_buffer[i], &output_buffer[i])); | |
| 648 | - else | |
| 649 | - _projectList(transform, &input_buffer[i], &output_buffer[i]); | |
| 613 | + if (Globals->parallelism) futures.addFuture(QtConcurrent::run(_projectList, transform, &input_buffer[i], &output_buffer[i])); | |
| 614 | + else _projectList( transform, &input_buffer[i], &output_buffer[i]); | |
| 650 | 615 | } |
| 651 | - | |
| 652 | - if (Globals->parallelism) | |
| 653 | - Globals->trackFutures(futures); | |
| 616 | + futures.waitForFinished(); | |
| 654 | 617 | |
| 655 | 618 | for (int i=0; i<src.size(); i++) dst.append(output_buffer[i]); |
| 656 | 619 | } |
| 657 | 620 | |
| 621 | + void projectUpdate(const TemplateList &src, TemplateList &dst) | |
| 622 | + { | |
| 623 | + if (!transform->timeVarying()) { | |
| 624 | + this->project(src, dst); | |
| 625 | + return; | |
| 626 | + } | |
| 627 | + this->transform->projectUpdate(src, dst); | |
| 628 | + } | |
| 629 | + | |
| 658 | 630 | |
| 659 | 631 | private: |
| 660 | 632 | ... | ... |
sdk/plugins/normalize.cpp
| ... | ... | @@ -14,6 +14,7 @@ |
| 14 | 14 | * limitations under the License. * |
| 15 | 15 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ |
| 16 | 16 | |
| 17 | +#include <QFutureSynchronizer> | |
| 17 | 18 | #include <QtConcurrentRun> |
| 18 | 19 | #include <opencv2/imgproc/imgproc.hpp> |
| 19 | 20 | #include <opencv2/highgui/highgui.hpp> |
| ... | ... | @@ -22,6 +23,7 @@ |
| 22 | 23 | |
| 23 | 24 | #include "core/common.h" |
| 24 | 25 | #include "core/opencvutils.h" |
| 26 | +#include "core/qtutils.h" | |
| 25 | 27 | |
| 26 | 28 | using namespace cv; |
| 27 | 29 | |
| ... | ... | @@ -119,18 +121,16 @@ private: |
| 119 | 121 | bv.push_back(Mat(1, dims, CV_64FC1)); |
| 120 | 122 | } |
| 121 | 123 | |
| 122 | - QList< QFuture<void> > futures; futures.reserve(dims); | |
| 124 | + QFutureSynchronizer<void> futures; | |
| 123 | 125 | const bool parallel = (data.size() > 1000) && Globals->parallelism; |
| 124 | - | |
| 125 | 126 | for (size_t c = 0; c < mv.size(); c++) { |
| 126 | 127 | for (int i=0; i<dims; i++) |
| 127 | - if (parallel) futures.append(QtConcurrent::run(_train, method, mv[c], &av[c], &bv[c], i)); | |
| 128 | - else _train (method, mv[c], &av[c], &bv[c], i); | |
| 128 | + if (parallel) futures.addFuture(QtConcurrent::run(_train, method, mv[c], &av[c], &bv[c], i)); | |
| 129 | + else _train (method, mv[c], &av[c], &bv[c], i); | |
| 129 | 130 | av[c] = av[c].reshape(1, data.first().m().rows); |
| 130 | 131 | bv[c] = bv[c].reshape(1, data.first().m().rows); |
| 131 | 132 | } |
| 132 | - | |
| 133 | - if (parallel) Globals->trackFutures(futures); | |
| 133 | + futures.waitForFinished(); | |
| 134 | 134 | |
| 135 | 135 | merge(av, a); |
| 136 | 136 | merge(bv, b); |
| ... | ... | @@ -224,6 +224,30 @@ class RowWiseMeanCenterTransform : public Transform |
| 224 | 224 | |
| 225 | 225 | BR_REGISTER(Transform, RowWiseMeanCenterTransform) |
| 226 | 226 | |
| 227 | +/*! | |
| 228 | + * \ingroup transforms | |
| 229 | + * \brief dst=sqrt(norm_L1(src)) proposed as RootSIFT in \cite Arandjelovic12 | |
| 230 | + * \author Josh Klontz \cite jklontz | |
| 231 | + */ | |
| 232 | +class RootNormTransform : public UntrainableTransform | |
| 233 | +{ | |
| 234 | + Q_OBJECT | |
| 235 | + | |
| 236 | + void project(const Template &src, Template &dst) const | |
| 237 | + { | |
| 238 | + const Mat &m = src; | |
| 239 | + dst.m() = Mat(m.rows, m.cols, m.type()); | |
| 240 | + for (int i=0; i<m.rows; i++) { | |
| 241 | + Mat temp; | |
| 242 | + cv::normalize(m.row(i), temp, 1, 0, NORM_L1); | |
| 243 | + cv::sqrt(temp, temp); | |
| 244 | + temp.copyTo(dst.m().row(i)); | |
| 245 | + } | |
| 246 | + } | |
| 247 | +}; | |
| 248 | + | |
| 249 | +BR_REGISTER(Transform, RootNormTransform) | |
| 250 | + | |
| 227 | 251 | } // namespace br |
| 228 | 252 | |
| 229 | 253 | #include "normalize.moc" | ... | ... |
sdk/plugins/output.cpp
| ... | ... | @@ -176,7 +176,7 @@ class rrOutput : public MatrixOutput |
| 176 | 176 | if (!byLine) files.append(queryFiles[i]); |
| 177 | 177 | |
| 178 | 178 | typedef QPair<float,int> Pair; |
| 179 | - foreach (const Pair &pair, Common::Sort(OpenCVUtils::matrixToVector(data.row(i)), true, limit)) { | |
| 179 | + foreach (const Pair &pair, Common::Sort(OpenCVUtils::matrixToVector<float>(data.row(i)), true, limit)) { | |
| 180 | 180 | if (pair.first < threshold) break; |
| 181 | 181 | File target = targetFiles[pair.second]; |
| 182 | 182 | target.set("Score", QString::number(pair.first)); |
| ... | ... | @@ -278,7 +278,7 @@ class rankOutput : public MatrixOutput |
| 278 | 278 | for (int i=0; i<queryFiles.size(); i++) { |
| 279 | 279 | typedef QPair<float,int> Pair; |
| 280 | 280 | int rank = 1; |
| 281 | - foreach (const Pair &pair, Common::Sort(OpenCVUtils::matrixToVector(data.row(i)), true)) { | |
| 281 | + foreach (const Pair &pair, Common::Sort(OpenCVUtils::matrixToVector<float>(data.row(i)), true)) { | |
| 282 | 282 | if(targetFiles[pair.second].get<QString>("Label") == queryFiles[i].get<QString>("Label")) { |
| 283 | 283 | ranks.append(rank); |
| 284 | 284 | positions.append(pair.second); | ... | ... |
sdk/plugins/quantize.cpp
| ... | ... | @@ -14,6 +14,8 @@ |
| 14 | 14 | * limitations under the License. * |
| 15 | 15 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ |
| 16 | 16 | |
| 17 | +#include <QFutureSynchronizer> | |
| 18 | +#include <QtConcurrentRun> | |
| 17 | 19 | #include <openbr_plugin.h> |
| 18 | 20 | |
| 19 | 21 | #include "core/opencvutils.h" |
| ... | ... | @@ -109,6 +111,145 @@ class PackTransform : public UntrainableTransform |
| 109 | 111 | |
| 110 | 112 | BR_REGISTER(Transform, PackTransform) |
| 111 | 113 | |
| 114 | +QVector<Mat> ProductQuantizationLUTs; | |
| 115 | + | |
| 116 | +/*! | |
| 117 | + * \ingroup distances | |
| 118 | + * \brief Distance in a product quantized space \cite jegou11 | |
| 119 | + * \author Josh Klontz | |
| 120 | + */ | |
| 121 | +class ProductQuantizationDistance : public Distance | |
| 122 | +{ | |
| 123 | + Q_OBJECT | |
| 124 | + Q_PROPERTY(bool bayesian READ get_bayesian WRITE set_bayesian RESET reset_bayesian STORED false) | |
| 125 | + BR_PROPERTY(bool, bayesian, false) | |
| 126 | + | |
| 127 | + float compare(const Template &a, const Template &b) const | |
| 128 | + { | |
| 129 | + float distance = 0; | |
| 130 | + for (int i=0; i<a.size(); i++) { | |
| 131 | + const int elements = a[i].total(); | |
| 132 | + const uchar *aData = a[i].data; | |
| 133 | + const uchar *bData = b[i].data; | |
| 134 | + const float *lut = (const float*)ProductQuantizationLUTs[i].data; | |
| 135 | + for (int j=0; j<elements; j++) | |
| 136 | + distance += lut[i*256*256 + aData[j]*256+bData[j]]; | |
| 137 | + } | |
| 138 | + if (!bayesian) distance = -log(distance+1); | |
| 139 | + return distance; | |
| 140 | + } | |
| 141 | +}; | |
| 142 | + | |
| 143 | +BR_REGISTER(Distance, ProductQuantizationDistance) | |
| 144 | + | |
| 145 | +/*! | |
| 146 | + * \ingroup transforms | |
| 147 | + * \brief Product quantization \cite jegou11 | |
| 148 | + * \author Josh Klontz \cite jklontz | |
| 149 | + */ | |
| 150 | +class ProductQuantizationTransform : public Transform | |
| 151 | +{ | |
| 152 | + Q_OBJECT | |
| 153 | + Q_PROPERTY(int n READ get_n WRITE set_n RESET reset_n STORED false) | |
| 154 | + Q_PROPERTY(bool bayesian READ get_bayesian WRITE set_bayesian RESET reset_bayesian STORED false) | |
| 155 | + BR_PROPERTY(int, n, 2) | |
| 156 | + BR_PROPERTY(bool, bayesian, false) | |
| 157 | + | |
| 158 | + int index; | |
| 159 | + QList<Mat> centers; | |
| 160 | + | |
| 161 | +public: | |
| 162 | + ProductQuantizationTransform() | |
| 163 | + { | |
| 164 | + index = ProductQuantizationLUTs.size(); | |
| 165 | + ProductQuantizationLUTs.append(Mat()); | |
| 166 | + } | |
| 167 | + | |
| 168 | +private: | |
| 169 | + static double likelihoodRatio(const QPair<int,int> &totals, const QList<int> &targets, const QList<int> &queries) | |
| 170 | + { | |
| 171 | + int positives = 0, negatives = 0; | |
| 172 | + foreach (int t, targets) | |
| 173 | + foreach (int q, queries) | |
| 174 | + if (t == q) positives++; | |
| 175 | + else negatives++; | |
| 176 | + return log(float(positives)/float(totals.first)) / (float(negatives)/float(totals.second)); | |
| 177 | + } | |
| 178 | + | |
| 179 | + void _train(const Mat &data, const QPair<int,int> &totals, Mat &lut, int i, const QList<int> &templateLabels) | |
| 180 | + { | |
| 181 | + Mat labels, center; | |
| 182 | + kmeans(data.colRange(i*n,(i+1)*n), 256, labels, TermCriteria(TermCriteria::MAX_ITER, 10, 0), 3, KMEANS_PP_CENTERS, center); | |
| 183 | + QList<int> clusterLabels = OpenCVUtils::matrixToVector<int>(labels); | |
| 184 | + QHash< int, QList<int> > clusters; // QHash<clusterLabel, QList<templateLabel> > | |
| 185 | + for (int i=0; i<clusterLabels.size(); i++) | |
| 186 | + clusters[clusterLabels[i]].append(templateLabels[i]); | |
| 187 | + | |
| 188 | + for (int j=0; j<256; j++) | |
| 189 | + for (int k=0; k<256; k++) | |
| 190 | + lut.at<float>(i,j*256+k) = bayesian ? likelihoodRatio(totals, clusters[j], clusters[k]) : | |
| 191 | + norm(center.row(j), center.row(k), NORM_L2); | |
| 192 | + centers[i] = center; | |
| 193 | + } | |
| 194 | + | |
| 195 | + void train(const TemplateList &src) | |
| 196 | + { | |
| 197 | + Mat data = OpenCVUtils::toMat(src.data()); | |
| 198 | + if (data.cols % n != 0) qFatal("Expected dimensionality to be divisible by n."); | |
| 199 | + const QList<int> templateLabels = src.labels<int>(); | |
| 200 | + int totalPositives = 0, totalNegatives = 0; | |
| 201 | + for (int i=0; i<templateLabels.size(); i++) | |
| 202 | + for (int j=0; j<templateLabels.size(); j++) | |
| 203 | + if (templateLabels[i] == templateLabels[j]) totalPositives++; | |
| 204 | + else totalNegatives++; | |
| 205 | + QPair<int,int> totals(totalPositives, totalNegatives); | |
| 206 | + | |
| 207 | + Mat &lut = ProductQuantizationLUTs[index]; | |
| 208 | + lut = Mat(data.cols/n, 256*256, CV_32FC1); | |
| 209 | + | |
| 210 | + for (int i=0; i<lut.rows; i++) | |
| 211 | + centers.append(Mat()); | |
| 212 | + | |
| 213 | + QFutureSynchronizer<void> futures; | |
| 214 | + for (int i=0; i<lut.rows; i++) { | |
| 215 | + if (Globals->parallelism) futures.addFuture(QtConcurrent::run(this, &ProductQuantizationTransform::_train, data, totals, lut, i, templateLabels)); | |
| 216 | + else _train (data, totals, lut, i, templateLabels); | |
| 217 | + } | |
| 218 | + futures.waitForFinished(); | |
| 219 | + } | |
| 220 | + | |
| 221 | + void project(const Template &src, Template &dst) const | |
| 222 | + { | |
| 223 | + Mat m = src.m().reshape(1, 1); | |
| 224 | + dst = Mat(1, m.cols/n, CV_8UC1); | |
| 225 | + for (int i=0; i<dst.m().cols; i++) { | |
| 226 | + int bestIndex = 0; | |
| 227 | + double bestDistance = std::numeric_limits<double>::max(); | |
| 228 | + Mat m_i = m.colRange(i*n, (i+1)*n); | |
| 229 | + for (int j=0; j<256; j++) { | |
| 230 | + double distance = norm(m_i, centers[index].row(j), NORM_L2); | |
| 231 | + if (distance < bestDistance) { | |
| 232 | + bestDistance = distance; | |
| 233 | + bestIndex = j; | |
| 234 | + } | |
| 235 | + } | |
| 236 | + dst.m().at<uchar>(0,i) = bestIndex; | |
| 237 | + } | |
| 238 | + } | |
| 239 | + | |
| 240 | + void store(QDataStream &stream) const | |
| 241 | + { | |
| 242 | + stream << centers << ProductQuantizationLUTs[index]; | |
| 243 | + } | |
| 244 | + | |
| 245 | + void load(QDataStream &stream) | |
| 246 | + { | |
| 247 | + stream >> centers >> ProductQuantizationLUTs[index]; | |
| 248 | + } | |
| 249 | +}; | |
| 250 | + | |
| 251 | +BR_REGISTER(Transform, ProductQuantizationTransform) | |
| 252 | + | |
| 112 | 253 | } // namespace br |
| 113 | 254 | |
| 114 | 255 | #include "quantize.moc" | ... | ... |
sdk/plugins/regions.cpp
sdk/plugins/sentence.cpp
0 โ 100644
| 1 | +#include <stdint.h> | |
| 2 | +#include <openbr_plugin.h> | |
| 3 | + | |
| 4 | +using namespace cv; | |
| 5 | + | |
| 6 | +namespace br | |
| 7 | +{ | |
| 8 | + | |
| 9 | +/*! | |
| 10 | + * \ingroup transforms | |
| 11 | + * \brief Ordered words | |
| 12 | + * \author Josh Klontz \cite jklontz | |
| 13 | + */ | |
| 14 | +class SentenceTransform : public UntrainableMetaTransform | |
| 15 | +{ | |
| 16 | + Q_OBJECT | |
| 17 | + | |
| 18 | + void project(const Template &src, Template &dst) const | |
| 19 | + { | |
| 20 | + QByteArray sentence; | |
| 21 | + QDataStream stream(&sentence, QIODevice::WriteOnly); | |
| 22 | + for (int i=0; i<src.size(); i++) { | |
| 23 | + const Mat &m = src[i]; | |
| 24 | + if (!m.data) continue; | |
| 25 | + stream.writeRawData((const char*)&i, 4); | |
| 26 | + stream.writeRawData((const char*)&m.rows, 4); | |
| 27 | + stream.writeRawData((const char*)&m.cols, 4); | |
| 28 | + stream.writeRawData((const char*)m.data, 4*m.rows*m.cols); | |
| 29 | + } | |
| 30 | + dst.file = src.file; | |
| 31 | + dst.m() = Mat(1, sentence.size(), CV_8UC1, sentence.data()).clone(); | |
| 32 | + } | |
| 33 | +}; | |
| 34 | + | |
| 35 | +BR_REGISTER(Transform, SentenceTransform) | |
| 36 | + | |
| 37 | +/*! | |
| 38 | + * \ingroup distances | |
| 39 | + * \brief Distance between sentences | |
| 40 | + * \author Josh Klontz \cite jklontz | |
| 41 | + */ | |
| 42 | +class SentenceSimilarityDistance : public Distance | |
| 43 | +{ | |
| 44 | + Q_OBJECT | |
| 45 | + | |
| 46 | + float compare(const Template &a, const Template &b) const | |
| 47 | + { | |
| 48 | + uchar *aBuffer = a.m().data; | |
| 49 | + uchar *bBuffer = b.m().data; | |
| 50 | + const uchar *aEnd = aBuffer + a.m().cols; | |
| 51 | + const uchar *bEnd = bBuffer + b.m().cols; | |
| 52 | + | |
| 53 | + int32_t aWord, bWord, aRows, bRows, aColumns, bColumns; | |
| 54 | + float *aData, *bData; | |
| 55 | + aWord = aRows = aColumns = -2; | |
| 56 | + bWord = bRows = bColumns = -1; | |
| 57 | + aData = bData = NULL; | |
| 58 | + | |
| 59 | + float distance = 0; | |
| 60 | + int comparisons = 0; | |
| 61 | + while (true) { | |
| 62 | + if (aWord < bWord) { | |
| 63 | + if (aBuffer == aEnd) return distance == 0 ? -std::numeric_limits<float>::max() : comparisons / distance; | |
| 64 | + aWord = *reinterpret_cast<int32_t*>(aBuffer); | |
| 65 | + aRows = *reinterpret_cast<int32_t*>(aBuffer+4); | |
| 66 | + aColumns = *reinterpret_cast<int32_t*>(aBuffer+8); | |
| 67 | + aData = reinterpret_cast<float*>(aBuffer+12); | |
| 68 | + aBuffer += 12 + 4*aRows*aColumns; | |
| 69 | + } else if (bWord < aWord) { | |
| 70 | + if (bBuffer == bEnd) return comparisons == 0 ? -std::numeric_limits<float>::max() : comparisons / distance; | |
| 71 | + bWord = *reinterpret_cast<int32_t*>(bBuffer); | |
| 72 | + bRows = *reinterpret_cast<int32_t*>(bBuffer+4); | |
| 73 | + bColumns = *reinterpret_cast<int32_t*>(bBuffer+8); | |
| 74 | + bData = reinterpret_cast<float*>(bBuffer+12); | |
| 75 | + bBuffer += 12 + 4*bRows*bColumns; | |
| 76 | + } else { | |
| 77 | + for (int i=0; i<aRows; i++) | |
| 78 | + for (int j=0; j<bRows; j++) | |
| 79 | + for (int k=0; k<aColumns; k++) | |
| 80 | + distance += pow(aBuffer[i*aColumns+k] - bBuffer[j*bColumns+k], 2.f); | |
| 81 | + comparisons += aRows * bRows * aColumns; | |
| 82 | + aWord = -2; | |
| 83 | + bWord = -1; | |
| 84 | + } | |
| 85 | + } | |
| 86 | + } | |
| 87 | +}; | |
| 88 | + | |
| 89 | +BR_REGISTER(Distance, SentenceSimilarityDistance) | |
| 90 | + | |
| 91 | +} // namespace br | |
| 92 | + | |
| 93 | +#include "sentence.moc" | ... | ... |
sdk/plugins/stream.cpp
0 โ 100644
| 1 | +#include <openbr_plugin.h> | |
| 2 | +#include <QReadWriteLock> | |
| 3 | +#include <QWaitCondition> | |
| 4 | +#include <QThreadPool> | |
| 5 | +#include <QSemaphore> | |
| 6 | +#include <QMap> | |
| 7 | + | |
| 8 | +#include "core/common.h" | |
| 9 | +#include "core/opencvutils.h" | |
| 10 | +#include "core/qtutils.h" | |
| 11 | + | |
| 12 | +#include "opencv/highgui.h" | |
| 13 | + | |
| 14 | +#include <iostream> | |
| 15 | + | |
| 16 | +using namespace cv; | |
| 17 | + | |
| 18 | +namespace br | |
| 19 | +{ | |
| 20 | + | |
| 21 | +class FrameData | |
| 22 | +{ | |
| 23 | +public: | |
| 24 | + int sequenceNumber; | |
| 25 | + TemplateList data; | |
| 26 | +}; | |
| 27 | + | |
| 28 | +// A buffer shared between adjacent processing stages in a stream | |
| 29 | +class SharedBuffer | |
| 30 | +{ | |
| 31 | +public: | |
| 32 | + SharedBuffer() {} | |
| 33 | + virtual ~SharedBuffer() {} | |
| 34 | + | |
| 35 | + virtual void addItem(FrameData * input)=0; | |
| 36 | + | |
| 37 | + virtual FrameData * getItem()=0; | |
| 38 | + | |
| 39 | + virtual void stoppedInput() =0; | |
| 40 | + virtual void startInput() = 0; | |
| 41 | +}; | |
| 42 | + | |
| 43 | +// For 1 - n boundaries, a buffer class with a single shared buffer, a mutex | |
| 44 | +// is used to serialize all access to the buffer. | |
| 45 | +class SingleBuffer : public SharedBuffer | |
| 46 | +{ | |
| 47 | +public: | |
| 48 | + SingleBuffer() { no_input = false; } | |
| 49 | + | |
| 50 | + void stoppedInput() | |
| 51 | + { | |
| 52 | + QMutexLocker bufferLock(&bufferGuard); | |
| 53 | + no_input = true; | |
| 54 | + // Release anything waiting for input items. | |
| 55 | + availableInput.wakeAll(); | |
| 56 | + } | |
| 57 | + | |
| 58 | + // There will be more input | |
| 59 | + void startInput() | |
| 60 | + { | |
| 61 | + QMutexLocker bufferLock(&bufferGuard); | |
| 62 | + no_input = false; | |
| 63 | + } | |
| 64 | + | |
| 65 | + void addItem(FrameData * input) | |
| 66 | + { | |
| 67 | + QMutexLocker bufferLock(&bufferGuard); | |
| 68 | + | |
| 69 | + buffer.append(input); | |
| 70 | + | |
| 71 | + availableInput.wakeOne(); | |
| 72 | + } | |
| 73 | + | |
| 74 | + FrameData * getItem() | |
| 75 | + { | |
| 76 | + QMutexLocker bufferLock(&bufferGuard); | |
| 77 | + | |
| 78 | + if (buffer.empty()) { | |
| 79 | + // If no further items will come we are done here | |
| 80 | + if (no_input) | |
| 81 | + return NULL; | |
| 82 | + // Wait for an item | |
| 83 | + availableInput.wait(&bufferGuard); | |
| 84 | + } | |
| 85 | + | |
| 86 | + // availableInput was signalled, but the buffer is still empty? We're done here. | |
| 87 | + if (buffer.empty()) | |
| 88 | + return NULL; | |
| 89 | + | |
| 90 | + FrameData * output = buffer.first(); | |
| 91 | + buffer.removeFirst(); | |
| 92 | + return output; | |
| 93 | + } | |
| 94 | + | |
| 95 | +private: | |
| 96 | + QMutex bufferGuard; | |
| 97 | + QWaitCondition availableInput; | |
| 98 | + bool no_input; | |
| 99 | + | |
| 100 | + QList<FrameData *> buffer; | |
| 101 | +}; | |
| 102 | + | |
| 103 | +// for n - 1 boundaries, multiple threads call addItem, the frames are | |
| 104 | +// sequenced based on FrameData::sequence_number, and calls to getItem | |
| 105 | +// receive them in that order | |
| 106 | +class SequencingBuffer : public SharedBuffer | |
| 107 | +{ | |
| 108 | +public: | |
| 109 | + SequencingBuffer() | |
| 110 | + { | |
| 111 | + no_input = false; | |
| 112 | + next_target = 0; | |
| 113 | + } | |
| 114 | + | |
| 115 | + void stoppedInput() | |
| 116 | + { | |
| 117 | + QMutexLocker bufferLock(&bufferGuard); | |
| 118 | + no_input = true; | |
| 119 | + // Release anything waiting for input items. | |
| 120 | + availableInput.wakeAll(); | |
| 121 | + } | |
| 122 | + | |
| 123 | + // There will be more input | |
| 124 | + void startInput() | |
| 125 | + { | |
| 126 | + QMutexLocker bufferLock(&bufferGuard); | |
| 127 | + no_input = false; | |
| 128 | + } | |
| 129 | + | |
| 130 | + void addItem(FrameData * input) | |
| 131 | + { | |
| 132 | + QMutexLocker bufferLock(&bufferGuard); | |
| 133 | + | |
| 134 | + buffer.insert(input->sequenceNumber, input); | |
| 135 | + | |
| 136 | + if (input->sequenceNumber == next_target) { | |
| 137 | + availableInput.wakeOne(); | |
| 138 | + } | |
| 139 | + } | |
| 140 | + | |
| 141 | + FrameData * getItem() | |
| 142 | + { | |
| 143 | + QMutexLocker bufferLock(&bufferGuard); | |
| 144 | + | |
| 145 | + if (buffer.empty() || buffer.begin().key() != this->next_target) { | |
| 146 | + if (buffer.empty() && no_input) { | |
| 147 | + next_target = 0; | |
| 148 | + return NULL; | |
| 149 | + } | |
| 150 | + availableInput.wait(&bufferGuard); | |
| 151 | + } | |
| 152 | + | |
| 153 | + // availableInput was signalled, but the buffer is empty? We're done here. | |
| 154 | + if (buffer.empty()) { | |
| 155 | + next_target = 0; | |
| 156 | + return NULL; | |
| 157 | + } | |
| 158 | + | |
| 159 | + QMap<int, FrameData *>::Iterator result = buffer.begin(); | |
| 160 | + //next_target++; | |
| 161 | + if (next_target != result.value()->sequenceNumber) { | |
| 162 | + qWarning("mismatched targets!"); | |
| 163 | + } | |
| 164 | + | |
| 165 | + next_target = next_target + 1; | |
| 166 | + | |
| 167 | + FrameData * output = result.value(); | |
| 168 | + buffer.erase(result); | |
| 169 | + return output; | |
| 170 | + } | |
| 171 | + | |
| 172 | +private: | |
| 173 | + QMutex bufferGuard; | |
| 174 | + QWaitCondition availableInput; | |
| 175 | + bool no_input; | |
| 176 | + | |
| 177 | + int next_target; | |
| 178 | + | |
| 179 | + QMap<int, FrameData *> buffer; | |
| 180 | +}; | |
| 181 | + | |
| 182 | +// For 1 - 1 boundaries, a double buffering scheme | |
| 183 | +// Producer/consumer read/write from separate buffers, and switch if their | |
| 184 | +// buffer runs out/overflows. Synchronization is handled by a read/write lock | |
| 185 | +// threads are "reading" if they are adding to/removing from their individual | |
| 186 | +// buffer, and writing if they access or swap with the other buffer. | |
| 187 | +class DoubleBuffer : public SharedBuffer | |
| 188 | +{ | |
| 189 | +public: | |
| 190 | + DoubleBuffer() | |
| 191 | + { | |
| 192 | + inputBuffer = &buffer1; | |
| 193 | + outputBuffer = &buffer2; | |
| 194 | + } | |
| 195 | + | |
| 196 | + void stoppedInput() | |
| 197 | + { | |
| 198 | + QWriteLocker bufferLock(&bufferGuard); | |
| 199 | + no_input = true; | |
| 200 | + // Release anything waiting for input items. | |
| 201 | + availableInput.wakeAll(); | |
| 202 | + } | |
| 203 | + | |
| 204 | + // There will be more input | |
| 205 | + void startInput() | |
| 206 | + { | |
| 207 | + QWriteLocker bufferLock(&bufferGuard); | |
| 208 | + no_input = false; | |
| 209 | + } | |
| 210 | + | |
| 211 | + // called from the producer thread | |
| 212 | + void addItem(FrameData * input) | |
| 213 | + { | |
| 214 | + QReadLocker readLock(&bufferGuard); | |
| 215 | + inputBuffer->append(input); | |
| 216 | + availableInput.wakeOne(); | |
| 217 | + } | |
| 218 | + | |
| 219 | + // Called from the consumer thread | |
| 220 | + FrameData * getItem() { | |
| 221 | + QReadLocker readLock(&bufferGuard); | |
| 222 | + | |
| 223 | + // There is something for us to get | |
| 224 | + if (!outputBuffer->empty()) { | |
| 225 | + FrameData * output = outputBuffer->first(); | |
| 226 | + outputBuffer->removeFirst(); | |
| 227 | + return output; | |
| 228 | + } | |
| 229 | + | |
| 230 | + // Outputbuffer is empty, try to swap with the input buffer, we need a | |
| 231 | + // write lock to do that. | |
| 232 | + readLock.unlock(); | |
| 233 | + QWriteLocker writeLock(&bufferGuard); | |
| 234 | + | |
| 235 | + // Nothing on the input buffer either? | |
| 236 | + if (inputBuffer->empty()) { | |
| 237 | + // If nothing else is coming, return null | |
| 238 | + if (no_input) | |
| 239 | + return NULL; | |
| 240 | + //otherwise, wait on the input buffer | |
| 241 | + availableInput.wait(&bufferGuard); | |
| 242 | + // Did we get woken up because no more input is coming? if so | |
| 243 | + // we're done here | |
| 244 | + if (no_input && inputBuffer->empty()) | |
| 245 | + return NULL; | |
| 246 | + } | |
| 247 | + | |
| 248 | + // input buffer is non-empty, so swap the buffers | |
| 249 | + std::swap(inputBuffer, outputBuffer); | |
| 250 | + | |
| 251 | + // Return a frame | |
| 252 | + FrameData * output = outputBuffer->first(); | |
| 253 | + outputBuffer->removeFirst(); | |
| 254 | + return output; | |
| 255 | + } | |
| 256 | + | |
| 257 | +private: | |
| 258 | + // The read-write lock. The thread adding to this buffer can add | |
| 259 | + // to the current input buffer if it has a read lock. The thread | |
| 260 | + // removing from this buffer can remove things from the current | |
| 261 | + // output buffer if it has a read lock, or swap the buffers if it | |
| 262 | + // has a write lock. | |
| 263 | + // Checking/modifying no_input requires a write lock. | |
| 264 | + QReadWriteLock bufferGuard; | |
| 265 | + QWaitCondition availableInput; | |
| 266 | + bool no_input; | |
| 267 | + | |
| 268 | + // The buffer that is currently being added to | |
| 269 | + QList<FrameData *> * inputBuffer; | |
| 270 | + // The buffer that is currently being removed from | |
| 271 | + QList<FrameData *> * outputBuffer; | |
| 272 | + | |
| 273 | + // The buffers pointed at by inputBuffer/outputBuffer | |
| 274 | + QList<FrameData *> buffer1; | |
| 275 | + QList<FrameData *> buffer2; | |
| 276 | +}; | |
| 277 | + | |
| 278 | + | |
| 279 | +// Interface for sequentially getting data from some data source. | |
| 280 | +// Initialized off of a template, can represent a video file (stored in the template's filename) | |
| 281 | +// or a set of images already loaded into memory stored as multiple matrices in an input template. | |
| 282 | +class DataSource | |
| 283 | +{ | |
| 284 | +public: | |
| 285 | + DataSource(int maxFrames=100) | |
| 286 | + { | |
| 287 | + for (int i=0; i < maxFrames;i++) | |
| 288 | + { | |
| 289 | + allFrames.addItem(new FrameData()); | |
| 290 | + } | |
| 291 | + allFrames.startInput(); | |
| 292 | + } | |
| 293 | + | |
| 294 | + virtual ~DataSource() | |
| 295 | + { | |
| 296 | + allFrames.stoppedInput(); | |
| 297 | + while (true) | |
| 298 | + { | |
| 299 | + FrameData * frame = allFrames.getItem(); | |
| 300 | + if (frame == NULL) | |
| 301 | + break; | |
| 302 | + delete frame; | |
| 303 | + } | |
| 304 | + } | |
| 305 | + | |
| 306 | + FrameData * getFrame() | |
| 307 | + { | |
| 308 | + FrameData * aFrame = allFrames.getItem(); | |
| 309 | + aFrame->data.clear(); | |
| 310 | + aFrame->sequenceNumber = -1; | |
| 311 | + | |
| 312 | + bool res = getNext(*aFrame); | |
| 313 | + if (!res) { | |
| 314 | + allFrames.addItem(aFrame); | |
| 315 | + return NULL; | |
| 316 | + } | |
| 317 | + return aFrame; | |
| 318 | + } | |
| 319 | + | |
| 320 | + void returnFrame(FrameData * inputFrame) | |
| 321 | + { | |
| 322 | + allFrames.addItem(inputFrame); | |
| 323 | + } | |
| 324 | + | |
| 325 | + virtual void close() = 0; | |
| 326 | + virtual bool open(Template & output) = 0; | |
| 327 | + virtual bool isOpen() = 0; | |
| 328 | + | |
| 329 | + virtual bool getNext(FrameData & input) = 0; | |
| 330 | + | |
| 331 | +protected: | |
| 332 | + DoubleBuffer allFrames; | |
| 333 | +}; | |
| 334 | + | |
| 335 | +// Read a video frame by frame using cv::VideoCapture | |
| 336 | +class VideoDataSource : public DataSource | |
| 337 | +{ | |
| 338 | +public: | |
| 339 | + VideoDataSource(int maxFrames) : DataSource(maxFrames) {} | |
| 340 | + | |
| 341 | + bool open(Template &input) | |
| 342 | + { | |
| 343 | + next_idx = 0; | |
| 344 | + basis = input; | |
| 345 | + video.open(input.file.name.toStdString()); | |
| 346 | + return video.isOpened(); | |
| 347 | + } | |
| 348 | + | |
| 349 | + bool isOpen() { return video.isOpened(); } | |
| 350 | + | |
| 351 | + void close() { video.release(); } | |
| 352 | + | |
| 353 | +private: | |
| 354 | + bool getNext(FrameData & output) | |
| 355 | + { | |
| 356 | + if (!isOpen()) | |
| 357 | + return false; | |
| 358 | + | |
| 359 | + output.data.append(Template(basis.file)); | |
| 360 | + output.data.last().append(cv::Mat()); | |
| 361 | + | |
| 362 | + output.sequenceNumber = next_idx; | |
| 363 | + next_idx++; | |
| 364 | + | |
| 365 | + bool res = video.read(output.data.last().last()); | |
| 366 | + if (!res) { | |
| 367 | + return false; | |
| 368 | + } | |
| 369 | + return true; | |
| 370 | + } | |
| 371 | + | |
| 372 | + cv::VideoCapture video; | |
| 373 | + Template basis; | |
| 374 | + int next_idx; | |
| 375 | +}; | |
| 376 | + | |
| 377 | +// Given a template as input, return its matrices one by one on subsequent calls | |
| 378 | +// to getNext | |
| 379 | +class TemplateDataSource : public DataSource | |
| 380 | +{ | |
| 381 | +public: | |
| 382 | + TemplateDataSource(int maxFrames) : DataSource(maxFrames) | |
| 383 | + { | |
| 384 | + current_idx = INT_MAX; | |
| 385 | + } | |
| 386 | + | |
| 387 | + bool open(Template &input) | |
| 388 | + { | |
| 389 | + basis = input; | |
| 390 | + current_idx = 0; | |
| 391 | + next_sequence = 0; | |
| 392 | + return isOpen(); | |
| 393 | + } | |
| 394 | + | |
| 395 | + bool isOpen() { return current_idx < basis.size() ; } | |
| 396 | + | |
| 397 | + void close() | |
| 398 | + { | |
| 399 | + current_idx = INT_MAX; | |
| 400 | + basis.clear(); | |
| 401 | + } | |
| 402 | + | |
| 403 | +private: | |
| 404 | + bool getNext(FrameData & output) | |
| 405 | + { | |
| 406 | + if (!isOpen()) | |
| 407 | + return false; | |
| 408 | + | |
| 409 | + output.data.append(basis[current_idx]); | |
| 410 | + current_idx++; | |
| 411 | + | |
| 412 | + output.sequenceNumber = next_sequence; | |
| 413 | + next_sequence++; | |
| 414 | + | |
| 415 | + return true; | |
| 416 | + } | |
| 417 | + | |
| 418 | + Template basis; | |
| 419 | + int current_idx; | |
| 420 | + int next_sequence; | |
| 421 | +}; | |
| 422 | + | |
| 423 | +// Given a template as input, create a VideoDataSource or a TemplateDataSource | |
| 424 | +// depending on whether or not it looks like the input template has already | |
| 425 | +// loaded frames into memory. | |
| 426 | +class DataSourceManager : public DataSource | |
| 427 | +{ | |
| 428 | +public: | |
| 429 | + DataSourceManager() | |
| 430 | + { | |
| 431 | + actualSource = NULL; | |
| 432 | + } | |
| 433 | + | |
| 434 | + ~DataSourceManager() | |
| 435 | + { | |
| 436 | + close(); | |
| 437 | + } | |
| 438 | + | |
| 439 | + void close() | |
| 440 | + { | |
| 441 | + if (actualSource) { | |
| 442 | + actualSource->close(); | |
| 443 | + delete actualSource; | |
| 444 | + actualSource = NULL; | |
| 445 | + } | |
| 446 | + } | |
| 447 | + | |
| 448 | + bool open(Template & input) | |
| 449 | + { | |
| 450 | + close(); | |
| 451 | + bool open_res = false; | |
| 452 | + // Input has no matrices? Its probably a video that hasn't been loaded yet | |
| 453 | + if (input.empty()) { | |
| 454 | + actualSource = new VideoDataSource(0); | |
| 455 | + open_res = actualSource->open(input); | |
| 456 | + } | |
| 457 | + else { | |
| 458 | + // create frame dealer | |
| 459 | + actualSource = new TemplateDataSource(0); | |
| 460 | + open_res = actualSource->open(input); | |
| 461 | + } | |
| 462 | + if (!isOpen()) { | |
| 463 | + delete actualSource; | |
| 464 | + actualSource = NULL; | |
| 465 | + return false; | |
| 466 | + } | |
| 467 | + return true; | |
| 468 | + } | |
| 469 | + | |
| 470 | + bool isOpen() { return !actualSource ? false : actualSource->isOpen(); } | |
| 471 | + | |
| 472 | +protected: | |
| 473 | + DataSource * actualSource; | |
| 474 | + bool getNext(FrameData & output) | |
| 475 | + { | |
| 476 | + return actualSource->getNext(output); | |
| 477 | + } | |
| 478 | + | |
| 479 | +}; | |
| 480 | + | |
| 481 | +class ProcessingStage : public QRunnable | |
| 482 | +{ | |
| 483 | + friend class StreamTransform; | |
| 484 | +public: | |
| 485 | + ProcessingStage(int nThreads = 1) | |
| 486 | + { | |
| 487 | + thread_count = nThreads; | |
| 488 | + activeThreads.release(thread_count); | |
| 489 | + setAutoDelete(false); | |
| 490 | + } | |
| 491 | + | |
| 492 | + void markStart() | |
| 493 | + { | |
| 494 | + activeThreads.acquire(); | |
| 495 | + } | |
| 496 | + | |
| 497 | + void waitStop() | |
| 498 | + { | |
| 499 | + // Wait until all threads have stopped | |
| 500 | + activeThreads.acquire(thread_count); | |
| 501 | + activeThreads.release(thread_count); | |
| 502 | + } | |
| 503 | + | |
| 504 | +protected: | |
| 505 | + void markStop() | |
| 506 | + { | |
| 507 | + activeThreads.release(); | |
| 508 | + } | |
| 509 | + QSemaphore activeThreads; | |
| 510 | + int thread_count; | |
| 511 | + | |
| 512 | + SharedBuffer * inputBuffer; | |
| 513 | + SharedBuffer * outputBuffer; | |
| 514 | + Transform * transform; | |
| 515 | + int stage_id; | |
| 516 | + | |
| 517 | +public: | |
| 518 | + // We should start, and enter a wait on input data | |
| 519 | + void run() | |
| 520 | + { | |
| 521 | + markStart(); | |
| 522 | + forever | |
| 523 | + { | |
| 524 | + FrameData * currentItem = inputBuffer->getItem(); | |
| 525 | + if (currentItem == NULL) | |
| 526 | + break; | |
| 527 | + // Project the input we got | |
| 528 | + transform->projectUpdate(currentItem->data); | |
| 529 | + // Add the result to the ouptut buffer | |
| 530 | + outputBuffer->addItem(currentItem); | |
| 531 | + } | |
| 532 | + markStop(); | |
| 533 | + } | |
| 534 | +}; | |
| 535 | + | |
| 536 | + | |
| 537 | +// No input buffer, instead we draw templates from some data source | |
| 538 | +// Will be operated by the main thread for the stream | |
| 539 | +class FirstStage : public ProcessingStage | |
| 540 | +{ | |
| 541 | +public: | |
| 542 | + DataSourceManager dataSource; | |
| 543 | + // Start drawing frames from the datasource. | |
| 544 | + void run() | |
| 545 | + { | |
| 546 | + forever | |
| 547 | + { | |
| 548 | + //FrameData * aFrame = dataSource.getNext(); | |
| 549 | + FrameData * aFrame = dataSource.getFrame(); | |
| 550 | + if (aFrame == NULL) | |
| 551 | + break; | |
| 552 | + outputBuffer->addItem(aFrame); | |
| 553 | + } | |
| 554 | + this->markStop(); | |
| 555 | + } | |
| 556 | +}; | |
| 557 | + | |
| 558 | +class LastStage : public ProcessingStage | |
| 559 | +{ | |
| 560 | +public: | |
| 561 | + TemplateList getOutput() | |
| 562 | + { | |
| 563 | + return collectedOutput; | |
| 564 | + } | |
| 565 | + | |
| 566 | +private: | |
| 567 | + TemplateList collectedOutput; | |
| 568 | +public: | |
| 569 | + DataSource * data; | |
| 570 | + void run() | |
| 571 | + { | |
| 572 | + forever | |
| 573 | + { | |
| 574 | + // Wait for input | |
| 575 | + FrameData * frame = inputBuffer->getItem(); | |
| 576 | + if (frame == NULL) | |
| 577 | + break; | |
| 578 | + // Just put the item on collectedOutput | |
| 579 | + collectedOutput.append(frame->data); | |
| 580 | + // Return the frame to the input frame buffer | |
| 581 | + data->returnFrame(frame); | |
| 582 | + } | |
| 583 | + this->markStop(); | |
| 584 | + } | |
| 585 | +}; | |
| 586 | + | |
| 587 | +class StreamTransform : public CompositeTransform | |
| 588 | +{ | |
| 589 | + Q_OBJECT | |
| 590 | + int threads_per_multi_stage; | |
| 591 | +public: | |
| 592 | + void train(const TemplateList & data) | |
| 593 | + { | |
| 594 | + foreach(Transform * transform, transforms) { | |
| 595 | + transform->train(data); | |
| 596 | + } | |
| 597 | + } | |
| 598 | + | |
| 599 | + bool timeVarying() const { return true; } | |
| 600 | + | |
| 601 | + void project(const Template &src, Template &dst) const | |
| 602 | + { | |
| 603 | + (void) src; (void) dst; | |
| 604 | + qFatal("nope"); | |
| 605 | + } | |
| 606 | + void project(const TemplateList & src, TemplateList & dst) const | |
| 607 | + { | |
| 608 | + (void) src; (void) dst; | |
| 609 | + qFatal("nope"); | |
| 610 | + } | |
| 611 | + | |
| 612 | + void projectUpdate(const Template &src, Template &dst) | |
| 613 | + { | |
| 614 | + (void) src; (void) dst; | |
| 615 | + qFatal("whatever"); | |
| 616 | + } | |
| 617 | + | |
| 618 | + // start processing | |
| 619 | + void projectUpdate(const TemplateList & src, TemplateList & dst) | |
| 620 | + { | |
| 621 | + if (src.size() != 1) | |
| 622 | + qFatal("Expected single template input to stream"); | |
| 623 | + | |
| 624 | + dst = src; | |
| 625 | + bool res = readStage.dataSource.open(dst[0]); | |
| 626 | + if (!res) { | |
| 627 | + qWarning("failed to stream template %s", qPrintable(dst[0].file.name)); | |
| 628 | + return; | |
| 629 | + } | |
| 630 | + | |
| 631 | + // Tell all buffers to expect input | |
| 632 | + for (int i=0; i < sharedBuffers.size(); i++) { | |
| 633 | + sharedBuffers[i]->startInput(); | |
| 634 | + } | |
| 635 | + | |
| 636 | + // Start our processing stages | |
| 637 | + for (int i=0; i < this->processingStages.size(); i++) { | |
| 638 | + int count = stage_variance[i] ? 1 : threads_per_multi_stage; | |
| 639 | + for (int j =0; j < count; j ++) processingThreads.start(processingStages[i]); | |
| 640 | + } | |
| 641 | + | |
| 642 | + // Start the final stage | |
| 643 | + processingThreads.start(&collectionStage); | |
| 644 | + | |
| 645 | + // Run the read stage ourselves | |
| 646 | + readStage.run(); | |
| 647 | + | |
| 648 | + // The read stage has stopped (since we ran the read stage). | |
| 649 | + // Step over the buffers, and call stoppedInput to tell the stage | |
| 650 | + // reading from each buffer that no more frames will be added after | |
| 651 | + // the current ones run out, then wait for the thread to finish. | |
| 652 | + for (int i =0; i < (sharedBuffers.size() - 1); i++) { | |
| 653 | + // Indicate that no more input will be available | |
| 654 | + sharedBuffers[i]->stoppedInput(); | |
| 655 | + | |
| 656 | + // Wait for the thread to finish. | |
| 657 | + this->processingStages[i]->waitStop(); | |
| 658 | + } | |
| 659 | + // Wait for the collection stage to finish | |
| 660 | + sharedBuffers.last()->stoppedInput(); | |
| 661 | + collectionStage.waitStop(); | |
| 662 | + | |
| 663 | + // dst is set to all output received by the final stage | |
| 664 | + dst = collectionStage.getOutput(); | |
| 665 | + } | |
| 666 | + | |
| 667 | + virtual void finalize(TemplateList & output) | |
| 668 | + { | |
| 669 | + (void) output; | |
| 670 | + // Not handling this yet -cao | |
| 671 | + } | |
| 672 | + | |
| 673 | + // Create and link stages | |
| 674 | + void init() | |
| 675 | + { | |
| 676 | + int thread_count = 0; | |
| 677 | + threads_per_multi_stage = 4; | |
| 678 | + stage_variance.reserve(transforms.size()); | |
| 679 | + foreach (const br::Transform *transform, transforms) { | |
| 680 | + stage_variance.append(transform->timeVarying()); | |
| 681 | + thread_count += transform->timeVarying() ? 1 : threads_per_multi_stage; | |
| 682 | + } | |
| 683 | + if (transforms.isEmpty()) return; | |
| 684 | + | |
| 685 | + // Set up the thread pool, 1 stage for each transform, as well as first | |
| 686 | + // and last stages, but the first stage is operated by the thread that | |
| 687 | + // calls project so the pool only needs nTransforms+1 total. | |
| 688 | + processingThreads.setMaxThreadCount(thread_count + 1); | |
| 689 | + | |
| 690 | + | |
| 691 | + // buffer 0 -- output buffer for the read stage, input buffer for | |
| 692 | + // first transform. Is that transform time-varying? | |
| 693 | + if (stage_variance[0]) | |
| 694 | + sharedBuffers.append(new DoubleBuffer()); | |
| 695 | + // If not, we can run multiple threads | |
| 696 | + else | |
| 697 | + sharedBuffers.append(new SingleBuffer()); | |
| 698 | + | |
| 699 | + readStage.outputBuffer = sharedBuffers.last(); | |
| 700 | + readStage.stage_id = 0; | |
| 701 | + | |
| 702 | + int next_stage_id = 1; | |
| 703 | + | |
| 704 | + int lastBufferIdx = 0; | |
| 705 | + for (int i =0; i < transforms.size(); i++) | |
| 706 | + { | |
| 707 | + // Set up this stage | |
| 708 | + processingStages.append(new ProcessingStage(stage_variance[i] ? 1 : threads_per_multi_stage)); | |
| 709 | + | |
| 710 | + processingStages.last()->stage_id = next_stage_id++; | |
| 711 | + processingStages.last()->inputBuffer = sharedBuffers[lastBufferIdx]; | |
| 712 | + lastBufferIdx++; | |
| 713 | + | |
| 714 | + // This stage's output buffer, next stage's input buffer. If this is | |
| 715 | + // the last transform, the next stage is the (time varying) collection | |
| 716 | + // stage | |
| 717 | + bool next_variance = (i+1) < transforms.size() ? stage_variance[i+1] : true; | |
| 718 | + bool current_variance = stage_variance[i]; | |
| 719 | + // if this is a single threaded stage | |
| 720 | + if (current_variance) | |
| 721 | + { | |
| 722 | + // 1 - 1 case | |
| 723 | + if (next_variance) | |
| 724 | + sharedBuffers.append(new DoubleBuffer()); | |
| 725 | + // 1 - n case | |
| 726 | + else | |
| 727 | + sharedBuffers.append(new SingleBuffer()); | |
| 728 | + } | |
| 729 | + // This is a multi-threaded stage | |
| 730 | + else | |
| 731 | + { | |
| 732 | + // If the next stage is single threaded, we need to sequence our | |
| 733 | + // output (n - 1 case) | |
| 734 | + if (next_variance) | |
| 735 | + sharedBuffers.append(new SequencingBuffer()); | |
| 736 | + // Otherwise, this is an n-n boundary and we don't need to | |
| 737 | + // adhere to any particular sequence | |
| 738 | + else | |
| 739 | + sharedBuffers.append(new SingleBuffer()); | |
| 740 | + } | |
| 741 | + processingStages.last()->outputBuffer = sharedBuffers.last(); | |
| 742 | + processingStages.last()->transform = transforms[i]; | |
| 743 | + } | |
| 744 | + | |
| 745 | + collectionStage.inputBuffer = sharedBuffers.last(); | |
| 746 | + collectionStage.data = &readStage.dataSource; | |
| 747 | + collectionStage.stage_id = next_stage_id; | |
| 748 | + } | |
| 749 | + | |
| 750 | + ~StreamTransform() | |
| 751 | + { | |
| 752 | + for (int i = 0; i < processingStages.size(); i++) { | |
| 753 | + delete processingStages[i]; | |
| 754 | + } | |
| 755 | + for (int i = 0; i < sharedBuffers.size(); i++) { | |
| 756 | + delete sharedBuffers[i]; | |
| 757 | + } | |
| 758 | + | |
| 759 | + } | |
| 760 | + | |
| 761 | +protected: | |
| 762 | + QList<bool> stage_variance; | |
| 763 | + | |
| 764 | + FirstStage readStage; | |
| 765 | + LastStage collectionStage; | |
| 766 | + | |
| 767 | + QList<ProcessingStage *> processingStages; | |
| 768 | + QList<SharedBuffer *> sharedBuffers; | |
| 769 | + | |
| 770 | + QThreadPool processingThreads; | |
| 771 | + | |
| 772 | + void _project(const Template &src, Template &dst) const | |
| 773 | + { | |
| 774 | + (void) src; (void) dst; | |
| 775 | + qFatal("nope"); | |
| 776 | + } | |
| 777 | + void _project(const TemplateList & src, TemplateList & dst) const | |
| 778 | + { | |
| 779 | + (void) src; (void) dst; | |
| 780 | + qFatal("nope"); | |
| 781 | + } | |
| 782 | +}; | |
| 783 | + | |
| 784 | +BR_REGISTER(Transform, StreamTransform) | |
| 785 | + | |
| 786 | + | |
| 787 | +} // namespace br | |
| 788 | + | |
| 789 | +#include "stream.moc" | |
| 790 | + | ... | ... |
sdk/plugins/validate.cpp
| 1 | +#include <QFutureSynchronizer> | |
| 1 | 2 | #include <QtConcurrentRun> |
| 2 | 3 | #include <openbr_plugin.h> |
| 3 | 4 | |
| ... | ... | @@ -34,16 +35,16 @@ class CrossValidateTransform : public MetaTransform |
| 34 | 35 | return; |
| 35 | 36 | } |
| 36 | 37 | |
| 37 | - QList< QFuture<void> > futures; | |
| 38 | + QFutureSynchronizer<void> futures; | |
| 38 | 39 | for (int i=0; i<numPartitions; i++) { |
| 39 | 40 | TemplateList partitionedData = data; |
| 40 | 41 | for (int j=partitionedData.size()-1; j>=0; j--) |
| 41 | 42 | if (partitions[j] == i) |
| 42 | 43 | partitionedData.removeAt(j); |
| 43 | - if (Globals->parallelism) futures.append(QtConcurrent::run(transforms[i], &Transform::train, partitionedData)); | |
| 44 | - else transforms[i]->train(partitionedData); | |
| 44 | + if (Globals->parallelism) futures.addFuture(QtConcurrent::run(transforms[i], &Transform::train, partitionedData)); | |
| 45 | + else transforms[i]->train(partitionedData); | |
| 45 | 46 | } |
| 46 | - Globals->trackFutures(futures); | |
| 47 | + futures.waitForFinished(); | |
| 47 | 48 | } |
| 48 | 49 | |
| 49 | 50 | void project(const Template &src, Template &dst) const | ... | ... |
sdk/plugins/youtube.cpp
share/openbr/openbr.bib
| ... | ... | @@ -33,7 +33,6 @@ |
| 33 | 33 | Author = {Lacey S. Best-Rowden}, |
| 34 | 34 | Howpublished = {https://github.com/lbestrowden}, |
| 35 | 35 | Title = {bestrow1 at msu.edu}} |
| 36 | - | |
| 37 | 36 | |
| 38 | 37 | % Software |
| 39 | 38 | @misc{OpenBR, |
| ... | ... | @@ -87,6 +86,14 @@ |
| 87 | 86 | |
| 88 | 87 | |
| 89 | 88 | % Papers |
| 89 | +@ inproceedings{Arandjelovic12, | |
| 90 | + Author={Arandjelovic, R. and Zisserman, A.}, | |
| 91 | + Booktitle={Computer Vision and Pattern Recognition (CVPR), 2012 IEEE Conference on}, | |
| 92 | + Title={Three things everyone should know to improve object retrieval}, | |
| 93 | + Month={June}, | |
| 94 | + Year={2012} | |
| 95 | + Pages={2911-2918}} | |
| 96 | + | |
| 90 | 97 | @inproceedings{belhumeur11, |
| 91 | 98 | Author = {Belhumeur, P.N. and Jacobs, D.W. and Kriegman, D.J. and Kumar, N.}, |
| 92 | 99 | Booktitle = {Computer Vision and Pattern Recognition (CVPR), 2011 IEEE Conference on}, | ... | ... |