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,6 +4,8 @@ endif() | ||
| 4 | 4 | ||
| 5 | add_executable(br br.cpp ${BR_RESOURCES}) | 5 | add_executable(br br.cpp ${BR_RESOURCES}) |
| 6 | target_link_libraries(br openbr ${CMAKE_THREAD_LIBS_INIT}) | 6 | target_link_libraries(br openbr ${CMAKE_THREAD_LIBS_INIT}) |
| 7 | +qt5_use_modules(br ${QT_DEPENDENCIES}) | ||
| 8 | + | ||
| 7 | install(TARGETS br RUNTIME DESTINATION bin) | 9 | install(TARGETS br RUNTIME DESTINATION bin) |
| 8 | 10 | ||
| 9 | add_test(NAME br_initialize WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND br) | 11 | add_test(NAME br_initialize WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND br) |
app/br/br.cpp
| @@ -14,6 +14,9 @@ | @@ -14,6 +14,9 @@ | ||
| 14 | * limitations under the License. * | 14 | * limitations under the License. * |
| 15 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | 15 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ |
| 16 | 16 | ||
| 17 | +#include <QCoreApplication> | ||
| 18 | +#include <QRunnable> | ||
| 19 | +#include <QThreadPool> | ||
| 17 | #include <stdio.h> | 20 | #include <stdio.h> |
| 18 | #include <stdlib.h> | 21 | #include <stdlib.h> |
| 19 | #include <string.h> | 22 | #include <string.h> |
| @@ -47,171 +50,193 @@ | @@ -47,171 +50,193 @@ | ||
| 47 | * \endcode | 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 | br_finalize(); | 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,8 +4,8 @@ if [ ! -f trainAgeRegression-PCSO.sh ]; then | ||
| 4 | exit | 4 | exit |
| 5 | fi | 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 | 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 | 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,10 +4,10 @@ if [ ! -f trainFaceRecognition-PCSO.sh ]; then | ||
| 4 | exit | 4 | exit |
| 5 | fi | 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 | 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 | 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,8 +4,8 @@ if [ ! -f trainGenderClassification-PCSO.sh ]; then | ||
| 4 | exit | 4 | exit |
| 5 | fi | 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 | 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 | 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,7 +236,7 @@ void BEE::makeMask(const QString &targetInput, const QString &queryInput, const | ||
| 236 | qDebug("Making mask from %s and %s to %s", qPrintable(targetInput), qPrintable(queryInput), qPrintable(mask)); | 236 | qDebug("Making mask from %s and %s to %s", qPrintable(targetInput), qPrintable(queryInput), qPrintable(mask)); |
| 237 | 237 | ||
| 238 | FileList targetFiles = TemplateList::fromGallery(targetInput).files(); | 238 | FileList targetFiles = TemplateList::fromGallery(targetInput).files(); |
| 239 | - FileList queryFiles = TemplateList::fromGallery(queryInput).files(); | 239 | + FileList queryFiles = (queryInput == ".") ? targetFiles : TemplateList::fromGallery(queryInput).files(); |
| 240 | QList<float> targetLabels = targetFiles.labels(); | 240 | QList<float> targetLabels = targetFiles.labels(); |
| 241 | QList<float> queryLabels = queryFiles.labels(); | 241 | QList<float> queryLabels = queryFiles.labels(); |
| 242 | QList<int> targetPartitions = targetFiles.crossValidationPartitions(); | 242 | QList<int> targetPartitions = targetFiles.crossValidationPartitions(); |
sdk/core/opencvutils.cpp
| @@ -143,6 +143,7 @@ Mat OpenCVUtils::toMatByRow(const QList<Mat> &src) | @@ -143,6 +143,7 @@ Mat OpenCVUtils::toMatByRow(const QList<Mat> &src) | ||
| 143 | 143 | ||
| 144 | int rows = 0; foreach (const Mat &m, src) rows += m.rows; | 144 | int rows = 0; foreach (const Mat &m, src) rows += m.rows; |
| 145 | int cols = src.first().cols; | 145 | int cols = src.first().cols; |
| 146 | + if (cols == 0) qFatal("Columnless matrix!"); | ||
| 146 | int type = src.first().type(); | 147 | int type = src.first().type(); |
| 147 | Mat dst(rows, cols, type); | 148 | Mat dst(rows, cols, type); |
| 148 | 149 | ||
| @@ -172,22 +173,6 @@ QString OpenCVUtils::elemToString(const Mat &m, int r, int c) | @@ -172,22 +173,6 @@ QString OpenCVUtils::elemToString(const Mat &m, int r, int c) | ||
| 172 | return "?"; | 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 | QString OpenCVUtils::matrixToString(const Mat &m) | 176 | QString OpenCVUtils::matrixToString(const Mat &m) |
| 192 | { | 177 | { |
| 193 | QString result; | 178 | QString result; |
| @@ -224,18 +209,6 @@ QStringList OpenCVUtils::matrixToStringList(const Mat &m) | @@ -224,18 +209,6 @@ QStringList OpenCVUtils::matrixToStringList(const Mat &m) | ||
| 224 | return results; | 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 | Point2f OpenCVUtils::toPoint(const QPointF &qPoint) | 212 | Point2f OpenCVUtils::toPoint(const QPointF &qPoint) |
| 240 | { | 213 | { |
| 241 | return Point2f(qPoint.x(), qPoint.y()); | 214 | return Point2f(qPoint.x(), qPoint.y()); |
sdk/core/opencvutils.h
| @@ -40,10 +40,38 @@ namespace OpenCVUtils | @@ -40,10 +40,38 @@ namespace OpenCVUtils | ||
| 40 | 40 | ||
| 41 | // From image | 41 | // From image |
| 42 | QString elemToString(const cv::Mat &m, int r, int c); | 42 | QString elemToString(const cv::Mat &m, int r, int c); |
| 43 | - float elemToFloat(const cv::Mat &m, int r, int c); | ||
| 44 | QString matrixToString(const cv::Mat &m); | 43 | QString matrixToString(const cv::Mat &m); |
| 45 | QStringList matrixToStringList(const cv::Mat &m); | 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 | // Conversions | 76 | // Conversions |
| 49 | cv::Point2f toPoint(const QPointF &qPoint); | 77 | cv::Point2f toPoint(const QPointF &qPoint); |
sdk/core/qtutils.cpp
| @@ -39,7 +39,7 @@ QStringList QtUtils::getFiles(QDir dir, bool recursive) | @@ -39,7 +39,7 @@ QStringList QtUtils::getFiles(QDir dir, bool recursive) | ||
| 39 | 39 | ||
| 40 | QStringList files; | 40 | QStringList files; |
| 41 | foreach (const QString &file, NaturalStringSort(dir.entryList(QDir::Files))) | 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 | if (!recursive) return files; | 44 | if (!recursive) return files; |
| 45 | 45 |
sdk/core/qtutils.h
| @@ -21,6 +21,7 @@ | @@ -21,6 +21,7 @@ | ||
| 21 | #include <QDir> | 21 | #include <QDir> |
| 22 | #include <QFile> | 22 | #include <QFile> |
| 23 | #include <QFileInfo> | 23 | #include <QFileInfo> |
| 24 | +#include <QFuture> | ||
| 24 | #include <QMap> | 25 | #include <QMap> |
| 25 | #include <QString> | 26 | #include <QString> |
| 26 | #include <QStringList> | 27 | #include <QStringList> |
sdk/frvt2012.cpp
| @@ -29,7 +29,12 @@ static const int frvt2012_template_size = 768; | @@ -29,7 +29,12 @@ static const int frvt2012_template_size = 768; | ||
| 29 | 29 | ||
| 30 | static void initialize(const string &configuration_location) | 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 | Globals->quiet = true; | 38 | Globals->quiet = true; |
| 34 | Globals->parallelism = 0; | 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,7 +103,7 @@ void br_fuse(int num_input_simmats, const char *input_simmats[], const char *mas | ||
| 103 | Fuse(QtUtils::toStringList(num_input_simmats, input_simmats), mask, normalization, fusion, output_simmat); | 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 | Context::initialize(argc, argv, sdk_path); | 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,7 +211,7 @@ BR_EXPORT void br_fuse(int num_input_simmats, const char *input_simmats[], const | ||
| 211 | * \brief Wraps br::Context::initialize() | 211 | * \brief Wraps br::Context::initialize() |
| 212 | * \see br_initialize_qt br_finalize | 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 | * \brief Wraps br::Context::initializeQt() | 217 | * \brief Wraps br::Context::initializeQt() |
sdk/openbr_export.cpp
| @@ -82,6 +82,7 @@ $ br -help | @@ -82,6 +82,7 @@ $ br -help | ||
| 82 | * -# Consider the free open source program <a href="http://wincdemu.sysprogs.org">WinCDEmu</a> if you need a program to mount ISO images. | 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 | * -# You will have to register with Microsoft after installation, but it's free. | 83 | * -# You will have to register with Microsoft after installation, but it's free. |
| 84 | * -# Grab any available <a href="http://www.microsoft.com/visualstudio/eng/downloads#d-visual-studio-2012-update">Visual Studio Updates</a>. | 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 | * -# <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 | * -# <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 | * -# During installation setup select "add CMake to PATH". | 87 | * -# During installation setup select "add CMake to PATH". |
| 87 | * -# <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>. | 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,19 +101,8 @@ $ br -help | ||
| 100 | * $ nmake install | 101 | * $ nmake install |
| 101 | * $ nmake clean | 102 | * $ nmake clean |
| 102 | * \endcode | 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 | * -# <a href="http://www.microsoft.com/en-us/download/confirmation.aspx?id=6812">Download Direct X Software Developement Kit</a> and install. | 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 | * -# 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>. | 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 | * -# Launch "Git Bash" from the Desktop and clone OpenBR: | 107 | * -# Launch "Git Bash" from the Desktop and clone OpenBR: |
| 118 | * \code | 108 | * \code |
| @@ -128,7 +118,7 @@ $ br -help | @@ -128,7 +118,7 @@ $ br -help | ||
| 128 | * $ cd C:\openbr | 118 | * $ cd C:\openbr |
| 129 | * $ mkdir build-msvc2012 | 119 | * $ mkdir build-msvc2012 |
| 130 | * $ cd build-msvc2012 | 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 | * $ nmake | 122 | * $ nmake |
| 133 | * $ nmake install | 123 | * $ nmake install |
| 134 | * \endcode | 124 | * \endcode |
| @@ -147,7 +137,7 @@ $ br -help | @@ -147,7 +137,7 @@ $ br -help | ||
| 147 | * -# Browse to your prexisting build directory "C:\openbr\build-msvc2012" then select "Next". | 137 | * -# Browse to your prexisting build directory "C:\openbr\build-msvc2012" then select "Next". |
| 148 | * -# Clear any text in the "arguments" box then select "Run CMake" then "Finish". | 138 | * -# Clear any text in the "arguments" box then select "Run CMake" then "Finish". |
| 149 | * -# 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. | 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 | * -# <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. | 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 | * -# From the VS2012 x64 Cross Tools Command Prompt: | 142 | * -# From the VS2012 x64 Cross Tools Command Prompt: |
| 153 | * \code | 143 | * \code |
| @@ -226,7 +216,7 @@ $ br -help | @@ -226,7 +216,7 @@ $ br -help | ||
| 226 | * -# Browse to your prexisting build directory "C:\openbr\build-mingw64" then select "Next". | 216 | * -# Browse to your prexisting build directory "C:\openbr\build-mingw64" then select "Next". |
| 227 | * -# Clear any text in the "arguments" box then select "Run CMake" then "Finish". | 217 | * -# Clear any text in the "arguments" box then select "Run CMake" then "Finish". |
| 228 | * -# 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. | 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 | * -# <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. | 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 | * -# From the MinGW-w64 Command Prompt: | 221 | * -# From the MinGW-w64 Command Prompt: |
| 232 | * \code | 222 | * \code |
| @@ -277,7 +267,7 @@ $ br -help | @@ -277,7 +267,7 @@ $ br -help | ||
| 277 | * $ cd build | 267 | * $ cd build |
| 278 | * $ cmake -DCMAKE_PREFIX_PATH=~/Qt5.0.1/5.0.1/clang_64 -DCMAKE_BUILD_TYPE=Release .. | 268 | * $ cmake -DCMAKE_PREFIX_PATH=~/Qt5.0.1/5.0.1/clang_64 -DCMAKE_BUILD_TYPE=Release .. |
| 279 | * $ make -j4 | 269 | * $ make -j4 |
| 280 | - * $ make install | 270 | + * $ sudo make install |
| 281 | * \endcode | 271 | * \endcode |
| 282 | * -# Hack OpenBR! | 272 | * -# Hack OpenBR! |
| 283 | * -# Open Qt Creator IDE | 273 | * -# Open Qt Creator IDE |
| @@ -289,12 +279,12 @@ $ br -help | @@ -289,12 +279,12 @@ $ br -help | ||
| 289 | * -# Browse to your prexisting build directory "~/openbr/build" then select "Continue". | 279 | * -# Browse to your prexisting build directory "~/openbr/build" then select "Continue". |
| 290 | * -# Select "Run CMake" then "Done". | 280 | * -# Select "Run CMake" then "Done". |
| 291 | * -# 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. | 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 | * \code | 283 | * \code |
| 294 | * $ cd openbr/build | 284 | * $ cd openbr/build |
| 295 | * $ make package | 285 | * $ make package |
| 296 | * \endcode | 286 | * \endcode |
| 297 | - * -# Build OpenBR documentation! | 287 | + * -# (Optional) Build OpenBR documentation! |
| 298 | * -# <a href="ftp://ftp.stack.nl/pub/users/dimitri/doxygen-1.8.2.src.tar.gz">Download Doxygen 1.8.2</a> and install: | 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 | * \code | 289 | * \code |
| 300 | * $ cd ~/Downloads | 290 | * $ cd ~/Downloads |
| @@ -367,7 +357,7 @@ $ br -help | @@ -367,7 +357,7 @@ $ br -help | ||
| 367 | * $ cd build | 357 | * $ cd build |
| 368 | * $ cmake -DCMAKE_PREFIX_PATH=~/Qt5.0.1/5.0.1/gcc_64 -DCMAKE_BUILD_TYPE=Release .. | 358 | * $ cmake -DCMAKE_PREFIX_PATH=~/Qt5.0.1/5.0.1/gcc_64 -DCMAKE_BUILD_TYPE=Release .. |
| 369 | * $ make -j4 | 359 | * $ make -j4 |
| 370 | - * $ make install | 360 | + * $ sudo make install |
| 371 | * \endcode | 361 | * \endcode |
| 372 | * -# Hack OpenBR! | 362 | * -# Hack OpenBR! |
| 373 | * -# Open Qt Creator IDE | 363 | * -# Open Qt Creator IDE |
| @@ -379,7 +369,7 @@ $ br -help | @@ -379,7 +369,7 @@ $ br -help | ||
| 379 | * -# Browse to your prexisting build directory "~/openbr/build" then select "Next". | 369 | * -# Browse to your prexisting build directory "~/openbr/build" then select "Next". |
| 380 | * -# Select "Run CMake" then "Finish". | 370 | * -# Select "Run CMake" then "Finish". |
| 381 | * -# 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. | 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 | * \code | 373 | * \code |
| 384 | * $ cd openbr/build | 374 | * $ cd openbr/build |
| 385 | * $ make package | 375 | * $ make package |
| @@ -434,7 +424,7 @@ $ br -help | @@ -434,7 +424,7 @@ $ br -help | ||
| 434 | * $ cd build-icc | 424 | * $ cd build-icc |
| 435 | * $ 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 .. | 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 | * $ make -j4 | 426 | * $ make -j4 |
| 437 | - * $ make install | 427 | + * $ sudo make install |
| 438 | * \endcode | 428 | * \endcode |
| 439 | * -# Hack OpenBR! | 429 | * -# Hack OpenBR! |
| 440 | * -# Open Qt Creator IDE | 430 | * -# Open Qt Creator IDE |
| @@ -446,7 +436,7 @@ $ br -help | @@ -446,7 +436,7 @@ $ br -help | ||
| 446 | * -# Browse to your prexisting build directory "~/openbr/build" then select "Next". | 436 | * -# Browse to your prexisting build directory "~/openbr/build" then select "Next". |
| 447 | * -# Select "Run CMake" then "Finish". | 437 | * -# Select "Run CMake" then "Finish". |
| 448 | * -# 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. | 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 | * \code | 440 | * \code |
| 451 | * $ cd openbr/build | 441 | * $ cd openbr/build |
| 452 | * $ make package | 442 | * $ make package |
sdk/openbr_plugin.cpp
| @@ -14,6 +14,7 @@ | @@ -14,6 +14,7 @@ | ||
| 14 | * limitations under the License. * | 14 | * limitations under the License. * |
| 15 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | 15 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ |
| 16 | 16 | ||
| 17 | +#include <QFutureSynchronizer> | ||
| 17 | #include <QMetaProperty> | 18 | #include <QMetaProperty> |
| 18 | #include <QPointF> | 19 | #include <QPointF> |
| 19 | #include <QRect> | 20 | #include <QRect> |
| @@ -28,6 +29,10 @@ | @@ -28,6 +29,10 @@ | ||
| 28 | #include <iostream> | 29 | #include <iostream> |
| 29 | #include <openbr_plugin.h> | 30 | #include <openbr_plugin.h> |
| 30 | 31 | ||
| 32 | +#ifndef BR_EMBEDDED | ||
| 33 | +#include <QApplication> | ||
| 34 | +#endif | ||
| 35 | + | ||
| 31 | #include "version.h" | 36 | #include "version.h" |
| 32 | #include "core/bee.h" | 37 | #include "core/bee.h" |
| 33 | #include "core/common.h" | 38 | #include "core/common.h" |
| @@ -385,6 +390,15 @@ TemplateList TemplateList::fromGallery(const br::File &gallery) | @@ -385,6 +390,15 @@ TemplateList TemplateList::fromGallery(const br::File &gallery) | ||
| 385 | QScopedPointer<Gallery> i(Gallery::make(file)); | 390 | QScopedPointer<Gallery> i(Gallery::make(file)); |
| 386 | TemplateList newTemplates = i->read(); | 391 | TemplateList newTemplates = i->read(); |
| 387 | newTemplates = newTemplates.mid(gallery.get<int>("pos", 0), gallery.get<int>("length", -1)); | 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 | if (gallery.get<bool>("reduce", false)) newTemplates = newTemplates.reduced(); | 402 | if (gallery.get<bool>("reduce", false)) newTemplates = newTemplates.reduced(); |
| 389 | const int crossValidate = gallery.get<int>("crossValidate"); | 403 | const int crossValidate = gallery.get<int>("crossValidate"); |
| 390 | if (crossValidate > 0) srand(0); | 404 | if (crossValidate > 0) srand(0); |
| @@ -770,27 +784,31 @@ int br::Context::timeRemaining() const | @@ -770,27 +784,31 @@ int br::Context::timeRemaining() const | ||
| 770 | return std::max(0, int((1 - p) / p * startTime.elapsed())) / 1000; | 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 | bool br::Context::checkSDKPath(const QString &sdkPath) | 787 | bool br::Context::checkSDKPath(const QString &sdkPath) |
| 782 | { | 788 | { |
| 783 | return QFileInfo(sdkPath + "/share/openbr/openbr.bib").exists(); | 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 | if (Globals == NULL) { | 807 | if (Globals == NULL) { |
| 789 | Globals = new Context(); | 808 | Globals = new Context(); |
| 790 | Globals->init(File()); | 809 | Globals->init(File()); |
| 791 | } | 810 | } |
| 792 | 811 | ||
| 793 | - Globals->coreApplication = QSharedPointer<QCoreApplication>(new QCoreApplication(argc, argv)); | ||
| 794 | initializeQt(sdkPath); | 812 | initializeQt(sdkPath); |
| 795 | 813 | ||
| 796 | #ifdef BR_DISTRIBUTED | 814 | #ifdef BR_DISTRIBUTED |
| @@ -853,7 +871,10 @@ void br::Context::initializeQt(QString sdkPath) | @@ -853,7 +871,10 @@ void br::Context::initializeQt(QString sdkPath) | ||
| 853 | 871 | ||
| 854 | void br::Context::finalize() | 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 | QList< QSharedPointer<Initializer> > initializers = Factory<Initializer>::makeAll(); | 878 | QList< QSharedPointer<Initializer> > initializers = Factory<Initializer>::makeAll(); |
| 858 | foreach (const QSharedPointer<Initializer> &initializer, initializers) | 879 | foreach (const QSharedPointer<Initializer> &initializer, initializers) |
| 859 | initializer->finalize(); | 880 | initializer->finalize(); |
| @@ -883,6 +904,11 @@ QString br::Context::scratchPath() | @@ -883,6 +904,11 @@ QString br::Context::scratchPath() | ||
| 883 | 904 | ||
| 884 | void br::Context::messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) | 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 | QString txt; | 912 | QString txt; |
| 887 | switch (type) { | 913 | switch (type) { |
| 888 | case QtDebugMsg: | 914 | case QtDebugMsg: |
| @@ -904,11 +930,8 @@ void br::Context::messageHandler(QtMsgType type, const QMessageLogContext &conte | @@ -904,11 +930,8 @@ void br::Context::messageHandler(QtMsgType type, const QMessageLogContext &conte | ||
| 904 | Globals->mostRecentMessage = txt; | 930 | Globals->mostRecentMessage = txt; |
| 905 | 931 | ||
| 906 | if (Globals->logFile.isWritable()) { | 932 | if (Globals->logFile.isWritable()) { |
| 907 | - static QMutex logLock; | ||
| 908 | - logLock.lock(); | ||
| 909 | Globals->logFile.write(qPrintable(txt)); | 933 | Globals->logFile.write(qPrintable(txt)); |
| 910 | Globals->logFile.flush(); | 934 | Globals->logFile.flush(); |
| 911 | - logLock.unlock(); | ||
| 912 | } | 935 | } |
| 913 | 936 | ||
| 914 | if (type == QtFatalMsg) { | 937 | if (type == QtFatalMsg) { |
| @@ -917,8 +940,6 @@ void br::Context::messageHandler(QtMsgType type, const QMessageLogContext &conte | @@ -917,8 +940,6 @@ void br::Context::messageHandler(QtMsgType type, const QMessageLogContext &conte | ||
| 917 | Globals->finalize(); | 940 | Globals->finalize(); |
| 918 | abort(); | 941 | abort(); |
| 919 | } | 942 | } |
| 920 | - | ||
| 921 | - QCoreApplication::processEvents(); // Used to retrieve messages before event loop starts | ||
| 922 | } | 943 | } |
| 923 | 944 | ||
| 924 | Context *br::Globals = NULL; | 945 | Context *br::Globals = NULL; |
| @@ -1147,14 +1168,12 @@ private: | @@ -1147,14 +1168,12 @@ private: | ||
| 1147 | for (int i=0; i<templatesList.size(); i++) | 1168 | for (int i=0; i<templatesList.size(); i++) |
| 1148 | templatesList[i] = Downsample(templatesList[i], transforms[i]); | 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 | for (int i=0; i<templatesList.size(); i++) { | 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 | void project(const Template &src, Template &dst) const | 1179 | void project(const Template &src, Template &dst) const |
| @@ -1250,42 +1269,51 @@ Transform *Transform::clone() const | @@ -1250,42 +1269,51 @@ Transform *Transform::clone() const | ||
| 1250 | return clone; | 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 | try { | 1274 | try { |
| 1256 | - transform->backProject(*dst, *src); | 1275 | + transform->project(*src, *dst); |
| 1257 | } catch (...) { | 1276 | } catch (...) { |
| 1258 | qWarning("Exception triggered when processing %s with transform %s", qPrintable(src->file.flat()), qPrintable(transform->objectName())); | 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 | void Transform::project(const TemplateList &src, TemplateList &dst) const | 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 | void Transform::backProject(const TemplateList &dst, TemplateList &src) const | 1319 | void Transform::backProject(const TemplateList &dst, TemplateList &src) const |
| @@ -1293,12 +1321,11 @@ void Transform::backProject(const TemplateList &dst, TemplateList &src) const | @@ -1293,12 +1321,11 @@ void Transform::backProject(const TemplateList &dst, TemplateList &src) const | ||
| 1293 | src.reserve(dst.size()); | 1321 | src.reserve(dst.size()); |
| 1294 | for (int i=0; i<dst.size(); i++) src.append(Template()); | 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 | for (int i=0; i<dst.size(); i++) | 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 | /* Distance - public methods */ | 1331 | /* Distance - public methods */ |
| @@ -1326,16 +1353,16 @@ void Distance::compare(const TemplateList &target, const TemplateList &query, Ou | @@ -1326,16 +1353,16 @@ void Distance::compare(const TemplateList &target, const TemplateList &query, Ou | ||
| 1326 | const bool stepTarget = target.size() > query.size(); | 1353 | const bool stepTarget = target.size() > query.size(); |
| 1327 | const int totalSize = std::max(target.size(), query.size()); | 1354 | const int totalSize = std::max(target.size(), query.size()); |
| 1328 | int stepSize = ceil(float(totalSize) / float(std::max(1, abs(Globals->parallelism)))); | 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 | for (int i=0; i<totalSize; i+=stepSize) { | 1357 | for (int i=0; i<totalSize; i+=stepSize) { |
| 1331 | const TemplateList &targets(stepTarget ? TemplateList(target.mid(i, stepSize)) : target); | 1358 | const TemplateList &targets(stepTarget ? TemplateList(target.mid(i, stepSize)) : target); |
| 1332 | const TemplateList &queries(stepTarget ? query : TemplateList(query.mid(i, stepSize))); | 1359 | const TemplateList &queries(stepTarget ? query : TemplateList(query.mid(i, stepSize))); |
| 1333 | const int targetOffset = stepTarget ? i : 0; | 1360 | const int targetOffset = stepTarget ? i : 0; |
| 1334 | const int queryOffset = stepTarget ? 0 : i; | 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 | QList<float> Distance::compare(const TemplateList &targets, const Template &query) const | 1368 | QList<float> Distance::compare(const TemplateList &targets, const Template &query) const |
sdk/openbr_plugin.h
| @@ -17,7 +17,6 @@ | @@ -17,7 +17,6 @@ | ||
| 17 | #ifndef __OPENBR_PLUGIN_H | 17 | #ifndef __OPENBR_PLUGIN_H |
| 18 | #define __OPENBR_PLUGIN_H | 18 | #define __OPENBR_PLUGIN_H |
| 19 | 19 | ||
| 20 | -#include <QCoreApplication> | ||
| 21 | #include <QDataStream> | 20 | #include <QDataStream> |
| 22 | #include <QDebug> | 21 | #include <QDebug> |
| 23 | #include <QDir> | 22 | #include <QDir> |
| @@ -466,7 +465,6 @@ private: | @@ -466,7 +465,6 @@ private: | ||
| 466 | class BR_EXPORT Context : public Object | 465 | class BR_EXPORT Context : public Object |
| 467 | { | 466 | { |
| 468 | Q_OBJECT | 467 | Q_OBJECT |
| 469 | - QSharedPointer<QCoreApplication> coreApplication; | ||
| 470 | QFile logFile; | 468 | QFile logFile; |
| 471 | 469 | ||
| 472 | public: | 470 | public: |
| @@ -624,12 +622,6 @@ public: | @@ -624,12 +622,6 @@ public: | ||
| 624 | int timeRemaining() const; | 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 | * \brief Returns \c true if \em sdkPath is valid, \c false otherwise. | 625 | * \brief Returns \c true if \em sdkPath is valid, \c false otherwise. |
| 634 | * \param sdkPath The path to <tt>share/openbr/openbr.bib</tt> | 626 | * \param sdkPath The path to <tt>share/openbr/openbr.bib</tt> |
| 635 | */ | 627 | */ |
| @@ -658,7 +650,7 @@ public: | @@ -658,7 +650,7 @@ public: | ||
| 658 | * \note <a href="http://qt-project.org/">Qt</a> users should instead call initializeQt(). | 650 | * \note <a href="http://qt-project.org/">Qt</a> users should instead call initializeQt(). |
| 659 | * \see initializeQt finalize | 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 | * \brief Alternative to initialize() for <a href="http://qt-project.org/">Qt</a> users. | 656 | * \brief Alternative to initialize() for <a href="http://qt-project.org/">Qt</a> users. |
| @@ -998,7 +990,7 @@ public: | @@ -998,7 +990,7 @@ public: | ||
| 998 | virtual void train(const TemplateList &data) = 0; /*!< \brief Train the transform. */ | 990 | virtual void train(const TemplateList &data) = 0; /*!< \brief Train the transform. */ |
| 999 | virtual void project(const Template &src, Template &dst) const = 0; /*!< \brief Apply the transform. */ | 991 | virtual void project(const Template &src, Template &dst) const = 0; /*!< \brief Apply the transform. */ |
| 1000 | virtual void project(const TemplateList &src, TemplateList &dst) const; /*!< \brief Apply the transform. */ | 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 | virtual void backProject(const TemplateList &dst, TemplateList &src) const; /*!< \brief Invert the transform. */ | 994 | virtual void backProject(const TemplateList &dst, TemplateList &src) const; /*!< \brief Invert the transform. */ |
| 1003 | 995 | ||
| 1004 | /*!< \brief Apply the transform, may update the transform's internal state */ | 996 | /*!< \brief Apply the transform, may update the transform's internal state */ |
| @@ -1113,6 +1105,7 @@ class BR_EXPORT TimeVaryingTransform : public Transform | @@ -1113,6 +1105,7 @@ class BR_EXPORT TimeVaryingTransform : public Transform | ||
| 1113 | { | 1105 | { |
| 1114 | Q_OBJECT | 1106 | Q_OBJECT |
| 1115 | 1107 | ||
| 1108 | +public: | ||
| 1116 | virtual bool timeVarying() const { return true; } | 1109 | virtual bool timeVarying() const { return true; } |
| 1117 | 1110 | ||
| 1118 | virtual void project(const Template &src, Template &dst) const | 1111 | virtual void project(const Template &src, Template &dst) const |
| @@ -1127,6 +1120,23 @@ class BR_EXPORT TimeVaryingTransform : public Transform | @@ -1127,6 +1120,23 @@ class BR_EXPORT TimeVaryingTransform : public Transform | ||
| 1127 | (void) dst; (void) src; | 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 | protected: | 1140 | protected: |
| 1131 | TimeVaryingTransform(bool independent = true, bool trainable = true) : Transform(independent, trainable) {} | 1141 | TimeVaryingTransform(bool independent = true, bool trainable = true) : Transform(independent, trainable) {} |
| 1132 | }; | 1142 | }; |
| @@ -1170,6 +1180,51 @@ protected: | @@ -1170,6 +1180,51 @@ protected: | ||
| 1170 | UntrainableMetaTransform() : UntrainableTransform(false) {} | 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,7 +32,6 @@ class AlgorithmsInitializer : public Initializer | ||
| 32 | { | 32 | { |
| 33 | // Face | 33 | // Face |
| 34 | Globals->abbreviations.insert("FaceRecognition", "FaceDetection!<FaceRecognitionRegistration>!<FaceRecognitionExtraction>+<FaceRecognitionEmbedding>+<FaceRecognitionQuantization>:MatchProbability(ByteL1)"); | 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 | Globals->abbreviations.insert("GenderClassification", "FaceDetection!<FaceClassificationRegistration>!<FaceClassificationExtraction>+<GenderClassifier>+Discard"); | 35 | Globals->abbreviations.insert("GenderClassification", "FaceDetection!<FaceClassificationRegistration>!<FaceClassificationExtraction>+<GenderClassifier>+Discard"); |
| 37 | Globals->abbreviations.insert("AgeRegression", "FaceDetection!<FaceClassificationRegistration>!<FaceClassificationExtraction>+<AgeRegressor>+Discard"); | 36 | Globals->abbreviations.insert("AgeRegression", "FaceDetection!<FaceClassificationRegistration>!<FaceClassificationExtraction>+<AgeRegressor>+Discard"); |
| 38 | Globals->abbreviations.insert("FaceQuality", "Open!Cascade(FrontalFace)+ASEFEyes+Affine(64,64,0.25,0.35)+ImageQuality+Cvt(Gray)+DFFS+Discard"); | 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,6 +42,7 @@ class AlgorithmsInitializer : public Initializer | ||
| 43 | Globals->abbreviations.insert("OpenBR", "FaceRecognition"); | 42 | Globals->abbreviations.insert("OpenBR", "FaceRecognition"); |
| 44 | Globals->abbreviations.insert("GenderEstimation", "GenderClassification"); | 43 | Globals->abbreviations.insert("GenderEstimation", "GenderClassification"); |
| 45 | Globals->abbreviations.insert("AgeEstimation", "AgeRegression"); | 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 | // Generic Image Processing | 47 | // Generic Image Processing |
| 48 | Globals->abbreviations.insert("SIFT", "Open+KeyPointDetector(SIFT)+KeyPointDescriptor(SIFT):KeyPointMatcher(BruteForce)"); | 48 | Globals->abbreviations.insert("SIFT", "Open+KeyPointDetector(SIFT)+KeyPointDescriptor(SIFT):KeyPointMatcher(BruteForce)"); |
| @@ -50,7 +50,7 @@ class AlgorithmsInitializer : public Initializer | @@ -50,7 +50,7 @@ class AlgorithmsInitializer : public Initializer | ||
| 50 | Globals->abbreviations.insert("SmallSIFT", "Open+LimitSize(512)+KeyPointDetector(SIFT)+KeyPointDescriptor(SIFT):KeyPointMatcher(BruteForce)"); | 50 | Globals->abbreviations.insert("SmallSIFT", "Open+LimitSize(512)+KeyPointDetector(SIFT)+KeyPointDescriptor(SIFT):KeyPointMatcher(BruteForce)"); |
| 51 | Globals->abbreviations.insert("SmallSURF", "Open+LimitSize(512)+KeyPointDetector(SURF)+KeyPointDescriptor(SURF):KeyPointMatcher(BruteForce)"); | 51 | Globals->abbreviations.insert("SmallSURF", "Open+LimitSize(512)+KeyPointDetector(SURF)+KeyPointDescriptor(SURF):KeyPointMatcher(BruteForce)"); |
| 52 | Globals->abbreviations.insert("ColorHist", "Open+LimitSize(512)!EnsureChannels(3)+SplitChannels+Hist(256,0,8)+Cat+Normalize(L1):L2"); | 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 | // Hash | 55 | // Hash |
| 56 | Globals->abbreviations.insert("FileName", "Name+Identity:Identical"); | 56 | Globals->abbreviations.insert("FileName", "Name+Identity:Identical"); |
sdk/plugins/cvt.cpp
| @@ -181,12 +181,17 @@ class RGTransform : public UntrainableTransform | @@ -181,12 +181,17 @@ class RGTransform : public UntrainableTransform | ||
| 181 | for (int i=0; i<m.rows; i++) | 181 | for (int i=0; i<m.rows; i++) |
| 182 | for (int j=0; j<m.cols; j++) { | 182 | for (int j=0; j<m.cols; j++) { |
| 183 | Vec3b v = m.at<Vec3b>(i,j); | 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 | dst.append(R); | 197 | dst.append(R); |
sdk/plugins/distance.cpp
| @@ -14,11 +14,13 @@ | @@ -14,11 +14,13 @@ | ||
| 14 | * limitations under the License. * | 14 | * limitations under the License. * |
| 15 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | 15 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ |
| 16 | 16 | ||
| 17 | +#include <QFutureSynchronizer> | ||
| 17 | #include <QtConcurrentRun> | 18 | #include <QtConcurrentRun> |
| 18 | #include <opencv2/imgproc/imgproc.hpp> | 19 | #include <opencv2/imgproc/imgproc.hpp> |
| 19 | #include <openbr_plugin.h> | 20 | #include <openbr_plugin.h> |
| 20 | 21 | ||
| 21 | #include "core/distance_sse.h" | 22 | #include "core/distance_sse.h" |
| 23 | +#include "core/qtutils.h" | ||
| 22 | 24 | ||
| 23 | using namespace cv; | 25 | using namespace cv; |
| 24 | 26 | ||
| @@ -154,11 +156,11 @@ class PipeDistance : public Distance | @@ -154,11 +156,11 @@ class PipeDistance : public Distance | ||
| 154 | 156 | ||
| 155 | void train(const TemplateList &data) | 157 | void train(const TemplateList &data) |
| 156 | { | 158 | { |
| 157 | - QList< QFuture<void> > futures; | 159 | + QFutureSynchronizer<void> futures; |
| 158 | foreach (br::Distance *distance, distances) | 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 | float compare(const Template &a, const Template &b) const | 166 | float compare(const Template &a, const Template &b) const |
sdk/plugins/eigen3.cpp
| @@ -145,15 +145,15 @@ protected: | @@ -145,15 +145,15 @@ protected: | ||
| 145 | 145 | ||
| 146 | if (keep < 1) { | 146 | if (keep < 1) { |
| 147 | // Keep eigenvectors that retain a certain energy percentage. | 147 | // Keep eigenvectors that retain a certain energy percentage. |
| 148 | - double totalEnergy = allEVals.sum(); | 148 | + const double totalEnergy = allEVals.sum(); |
| 149 | if (totalEnergy == 0) { | 149 | if (totalEnergy == 0) { |
| 150 | keep = 0; | 150 | keep = 0; |
| 151 | } else { | 151 | } else { |
| 152 | double currentEnergy = 0; | 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 | keep = i - drop; | 158 | keep = i - drop; |
| 159 | } | 159 | } |
sdk/plugins/format.cpp
| @@ -242,12 +242,11 @@ class DefaultFormat : public Format | @@ -242,12 +242,11 @@ class DefaultFormat : public Format | ||
| 242 | 242 | ||
| 243 | void write(const Template &t) const | 243 | void write(const Template &t) const |
| 244 | { | 244 | { |
| 245 | - if (t.size() != 1) { | 245 | + if (t.size() > 1) { |
| 246 | videoFormat videoWriter; | 246 | videoFormat videoWriter; |
| 247 | videoWriter.file = file; | 247 | videoWriter.file = file; |
| 248 | videoWriter.write(t); | 248 | videoWriter.write(t); |
| 249 | - } | ||
| 250 | - else { | 249 | + } else if (t.size() == 1) { |
| 251 | imwrite(file.name.toStdString(), t); | 250 | imwrite(file.name.toStdString(), t); |
| 252 | } | 251 | } |
| 253 | } | 252 | } |
| @@ -565,7 +564,6 @@ class webcamFormat : public Format | @@ -565,7 +564,6 @@ class webcamFormat : public Format | ||
| 565 | 564 | ||
| 566 | BR_REGISTER(Format, webcamFormat) | 565 | BR_REGISTER(Format, webcamFormat) |
| 567 | 566 | ||
| 568 | -#ifndef BR_EMBEDDED | ||
| 569 | /*! | 567 | /*! |
| 570 | * \ingroup formats | 568 | * \ingroup formats |
| 571 | * \brief Decodes images from Base64 xml | 569 | * \brief Decodes images from Base64 xml |
| @@ -578,13 +576,15 @@ class xmlFormat : public Format | @@ -578,13 +576,15 @@ class xmlFormat : public Format | ||
| 578 | 576 | ||
| 579 | Template read() const | 577 | Template read() const |
| 580 | { | 578 | { |
| 579 | + Template t; | ||
| 580 | + | ||
| 581 | +#ifndef BR_EMBEDDED | ||
| 581 | QDomDocument doc(file); | 582 | QDomDocument doc(file); |
| 582 | QFile f(file); | 583 | QFile f(file); |
| 583 | if (!f.open(QIODevice::ReadOnly)) qFatal("Unable to open %s for reading.", qPrintable(file.flat())); | 584 | if (!f.open(QIODevice::ReadOnly)) qFatal("Unable to open %s for reading.", qPrintable(file.flat())); |
| 584 | if (!doc.setContent(&f)) qFatal("Unable to parse %s.", qPrintable(file.flat())); | 585 | if (!doc.setContent(&f)) qFatal("Unable to parse %s.", qPrintable(file.flat())); |
| 585 | f.close(); | 586 | f.close(); |
| 586 | 587 | ||
| 587 | - Template t; | ||
| 588 | QDomElement docElem = doc.documentElement(); | 588 | QDomElement docElem = doc.documentElement(); |
| 589 | QDomNode subject = docElem.firstChild(); | 589 | QDomNode subject = docElem.firstChild(); |
| 590 | while (!subject.isNull()) { | 590 | while (!subject.isNull()) { |
| @@ -620,6 +620,7 @@ class xmlFormat : public Format | @@ -620,6 +620,7 @@ class xmlFormat : public Format | ||
| 620 | if (current.month() < dob.month()) age--; | 620 | if (current.month() < dob.month()) age--; |
| 621 | t.file.set("Age", age); | 621 | t.file.set("Age", age); |
| 622 | } | 622 | } |
| 623 | +#endif // BR_EMBEDDED | ||
| 623 | 624 | ||
| 624 | return t; | 625 | return t; |
| 625 | } | 626 | } |
| @@ -632,7 +633,6 @@ class xmlFormat : public Format | @@ -632,7 +633,6 @@ class xmlFormat : public Format | ||
| 632 | }; | 633 | }; |
| 633 | 634 | ||
| 634 | BR_REGISTER(Format, xmlFormat) | 635 | BR_REGISTER(Format, xmlFormat) |
| 635 | -#endif // BR_EMBEDDED | ||
| 636 | 636 | ||
| 637 | } // namespace br | 637 | } // namespace br |
| 638 | 638 |
sdk/plugins/gallery.cpp
| @@ -14,6 +14,7 @@ | @@ -14,6 +14,7 @@ | ||
| 14 | * limitations under the License. * | 14 | * limitations under the License. * |
| 15 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | 15 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ |
| 16 | 16 | ||
| 17 | +#include <QtConcurrentRun> | ||
| 17 | #ifndef BR_EMBEDDED | 18 | #ifndef BR_EMBEDDED |
| 18 | #include <QNetworkAccessManager> | 19 | #include <QNetworkAccessManager> |
| 19 | #include <QNetworkReply> | 20 | #include <QNetworkReply> |
| @@ -103,9 +104,14 @@ class EmptyGallery : public Gallery | @@ -103,9 +104,14 @@ class EmptyGallery : public Gallery | ||
| 103 | 104 | ||
| 104 | // Add immediate subfolders | 105 | // Add immediate subfolders |
| 105 | QDir dir(file); | 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 | // Add root folder | 116 | // Add root folder |
| 111 | foreach (const QString &fileName, QtUtils::getFiles(file.name, false)) | 117 | foreach (const QString &fileName, QtUtils::getFiles(file.name, false)) |
| @@ -130,6 +136,15 @@ class EmptyGallery : public Gallery | @@ -130,6 +136,15 @@ class EmptyGallery : public Gallery | ||
| 130 | format->write(t); | 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 | BR_REGISTER(Gallery, EmptyGallery) | 150 | BR_REGISTER(Gallery, EmptyGallery) |
| @@ -662,7 +677,7 @@ class googleGallery : public Gallery | @@ -662,7 +677,7 @@ class googleGallery : public Gallery | ||
| 662 | QNetworkReply *reply = networkAccessManager.get(request); | 677 | QNetworkReply *reply = networkAccessManager.get(request); |
| 663 | 678 | ||
| 664 | while (!reply->isFinished()) | 679 | while (!reply->isFinished()) |
| 665 | - QCoreApplication::processEvents(); | 680 | + QThread::yieldCurrentThread(); |
| 666 | 681 | ||
| 667 | QString data(reply->readAll()); | 682 | QString data(reply->readAll()); |
| 668 | delete reply; | 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,7 +111,7 @@ class RankTransform : public UntrainableTransform | ||
| 111 | assert(m.channels() == 1); | 111 | assert(m.channels() == 1); |
| 112 | dst = Mat(m.rows, m.cols, CV_32FC1); | 112 | dst = Mat(m.rows, m.cols, CV_32FC1); |
| 113 | typedef QPair<float,int> Tuple; | 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 | float prevValue = 0; | 116 | float prevValue = 0; |
| 117 | int prevRank = 0; | 117 | int prevRank = 0; |
sdk/plugins/integral.cpp
| @@ -28,7 +28,7 @@ BR_REGISTER(Transform, IntegralTransform) | @@ -28,7 +28,7 @@ BR_REGISTER(Transform, IntegralTransform) | ||
| 28 | 28 | ||
| 29 | /*! | 29 | /*! |
| 30 | * \ingroup transforms | 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 | * \author Josh Klontz \cite jklontz | 32 | * \author Josh Klontz \cite jklontz |
| 33 | */ | 33 | */ |
| 34 | class IntegralSamplerTransform : public UntrainableTransform | 34 | class IntegralSamplerTransform : public UntrainableTransform |
| @@ -38,9 +38,9 @@ class IntegralSamplerTransform : public UntrainableTransform | @@ -38,9 +38,9 @@ class IntegralSamplerTransform : public UntrainableTransform | ||
| 38 | Q_PROPERTY(float scaleFactor READ get_scaleFactor WRITE set_scaleFactor RESET reset_scaleFactor STORED false) | 38 | Q_PROPERTY(float scaleFactor READ get_scaleFactor WRITE set_scaleFactor RESET reset_scaleFactor STORED false) |
| 39 | Q_PROPERTY(float stepFactor READ get_stepFactor WRITE set_stepFactor RESET reset_stepFactor STORED false) | 39 | Q_PROPERTY(float stepFactor READ get_stepFactor WRITE set_stepFactor RESET reset_stepFactor STORED false) |
| 40 | Q_PROPERTY(int minSize READ get_minSize WRITE set_minSize RESET reset_minSize STORED false) | 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 | BR_PROPERTY(float, scaleFactor, 1.5) | 42 | BR_PROPERTY(float, scaleFactor, 1.5) |
| 43 | - BR_PROPERTY(float, stepFactor, 0.25) | 43 | + BR_PROPERTY(float, stepFactor, 0.75) |
| 44 | BR_PROPERTY(int, minSize, 8) | 44 | BR_PROPERTY(int, minSize, 8) |
| 45 | 45 | ||
| 46 | void project(const Template &src, Template &dst) const | 46 | void project(const Template &src, Template &dst) const |
| @@ -53,22 +53,23 @@ class IntegralSamplerTransform : public UntrainableTransform | @@ -53,22 +53,23 @@ class IntegralSamplerTransform : public UntrainableTransform | ||
| 53 | const int rowStep = channels * m.cols; | 53 | const int rowStep = channels * m.cols; |
| 54 | 54 | ||
| 55 | int descriptors = 0; | 55 | int descriptors = 0; |
| 56 | - int currentSize = min(m.rows, m.cols)-1; | 56 | + float idealSize = min(m.rows, m.cols)-1; |
| 57 | for (int scale=0; scale<scales; scale++) { | 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 | Mat n(descriptors, channels, CV_32FC1); | 64 | Mat n(descriptors, channels, CV_32FC1); |
| 65 | 65 | ||
| 66 | const qint32 *dataIn = (qint32*)m.data; | 66 | const qint32 *dataIn = (qint32*)m.data; |
| 67 | float *dataOut = (float*)n.data; | 67 | float *dataOut = (float*)n.data; |
| 68 | - currentSize = min(m.rows, m.cols)-1; | 68 | + idealSize = min(m.rows, m.cols)-1; |
| 69 | int index = 0; | 69 | int index = 0; |
| 70 | for (int scale=0; scale<scales; scale++) { | 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 | for (int i=currentSize; i<m.rows; i+=currentStep) { | 73 | for (int i=currentSize; i<m.rows; i+=currentStep) { |
| 73 | for (int j=currentSize; j<m.cols; j+=currentStep) { | 74 | for (int j=currentSize; j<m.cols; j+=currentStep) { |
| 74 | InputDescriptor a(dataIn+((i-currentSize)*rowStep+(j-currentSize)*channels), channels, 1); | 75 | InputDescriptor a(dataIn+((i-currentSize)*rowStep+(j-currentSize)*channels), channels, 1); |
| @@ -80,10 +81,13 @@ class IntegralSamplerTransform : public UntrainableTransform | @@ -80,10 +81,13 @@ class IntegralSamplerTransform : public UntrainableTransform | ||
| 80 | index++; | 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 | dst.m() = n; | 91 | dst.m() = n; |
| 88 | } | 92 | } |
| 89 | }; | 93 | }; |
| @@ -133,38 +137,51 @@ class WordWiseTransform : public Transform | @@ -133,38 +137,51 @@ class WordWiseTransform : public Transform | ||
| 133 | Q_OBJECT | 137 | Q_OBJECT |
| 134 | Q_PROPERTY(br::Transform* getWords READ get_getWords WRITE set_getWords RESET reset_getWords) | 138 | Q_PROPERTY(br::Transform* getWords READ get_getWords WRITE set_getWords RESET reset_getWords) |
| 135 | Q_PROPERTY(br::Transform* byWord READ get_byWord WRITE set_byWord RESET reset_byWord) | 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 | BR_PROPERTY(br::Transform*, getWords, NULL) | 141 | BR_PROPERTY(br::Transform*, getWords, NULL) |
| 137 | BR_PROPERTY(br::Transform*, byWord, NULL) | 142 | BR_PROPERTY(br::Transform*, byWord, NULL) |
| 143 | + BR_PROPERTY(int, numWords, 0) | ||
| 138 | 144 | ||
| 139 | void train(const TemplateList &data) | 145 | void train(const TemplateList &data) |
| 140 | { | 146 | { |
| 141 | getWords->train(data); | 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 | double minVal, maxVal; | 153 | double minVal, maxVal; |
| 149 | minMaxLoc(t, &minVal, &maxVal); | 154 | minMaxLoc(t, &minVal, &maxVal); |
| 150 | numWords = max(numWords, int(maxVal)+1); | 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 | void project(const Template &src, Template &dst) const | 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,6 +14,7 @@ | ||
| 14 | * limitations under the License. * | 14 | * limitations under the License. * |
| 15 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | 15 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ |
| 16 | 16 | ||
| 17 | +#include <QFutureSynchronizer> | ||
| 17 | #include <QtConcurrentRun> | 18 | #include <QtConcurrentRun> |
| 18 | #include <openbr_plugin.h> | 19 | #include <openbr_plugin.h> |
| 19 | 20 | ||
| @@ -60,76 +61,6 @@ static void _train(Transform *transform, const TemplateList *data) | @@ -60,76 +61,6 @@ static void _train(Transform *transform, const TemplateList *data) | ||
| 60 | transform->train(*data); | 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 | * \ingroup Transforms | 65 | * \ingroup Transforms |
| 135 | * \brief Transforms in series. | 66 | * \brief Transforms in series. |
| @@ -146,16 +77,13 @@ class PipeTransform : public CompositeTransform | @@ -146,16 +77,13 @@ class PipeTransform : public CompositeTransform | ||
| 146 | 77 | ||
| 147 | void train(const TemplateList &data) | 78 | void train(const TemplateList &data) |
| 148 | { | 79 | { |
| 149 | - acquireStep(); | ||
| 150 | - | ||
| 151 | TemplateList copy(data); | 80 | TemplateList copy(data); |
| 152 | for (int i=0; i<transforms.size(); i++) { | 81 | for (int i=0; i<transforms.size(); i++) { |
| 82 | + fprintf(stderr, "%s training... ", qPrintable(transforms[i]->objectName())); | ||
| 153 | transforms[i]->train(copy); | 83 | transforms[i]->train(copy); |
| 84 | + fprintf(stderr, "projecting...\n"); | ||
| 154 | copy >> *transforms[i]; | 85 | copy >> *transforms[i]; |
| 155 | - incrementStep(); | ||
| 156 | } | 86 | } |
| 157 | - | ||
| 158 | - releaseStep(); | ||
| 159 | } | 87 | } |
| 160 | 88 | ||
| 161 | void backProject(const Template &dst, Template &src) const | 89 | void backProject(const Template &dst, Template &src) const |
| @@ -284,6 +212,38 @@ BR_REGISTER(Transform, ExpandTransform) | @@ -284,6 +212,38 @@ BR_REGISTER(Transform, ExpandTransform) | ||
| 284 | 212 | ||
| 285 | /*! | 213 | /*! |
| 286 | * \ingroup transforms | 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 | * \brief Transforms in parallel. | 247 | * \brief Transforms in parallel. |
| 288 | * \author Josh Klontz \cite jklontz | 248 | * \author Josh Klontz \cite jklontz |
| 289 | * | 249 | * |
| @@ -297,13 +257,12 @@ class ForkTransform : public CompositeTransform | @@ -297,13 +257,12 @@ class ForkTransform : public CompositeTransform | ||
| 297 | 257 | ||
| 298 | void train(const TemplateList &data) | 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 | for (int i=0; i<transforms.size(); i++) { | 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 | void backProject(const Template &dst, Template &src) const {Transform::backProject(dst, src);} | 268 | void backProject(const Template &dst, Template &src) const {Transform::backProject(dst, src);} |
| @@ -626,6 +585,15 @@ public: | @@ -626,6 +585,15 @@ public: | ||
| 626 | // Process the single elemnt templates in parallel if parallelism is enabled. | 585 | // Process the single elemnt templates in parallel if parallelism is enabled. |
| 627 | void project(const TemplateList &src, TemplateList &dst) const | 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 | // Pre-allocate output for each template | 597 | // Pre-allocate output for each template |
| 630 | QList<TemplateList> output_buffer; | 598 | QList<TemplateList> output_buffer; |
| 631 | output_buffer.reserve(src.size()); | 599 | output_buffer.reserve(src.size()); |
| @@ -639,22 +607,26 @@ public: | @@ -639,22 +607,26 @@ public: | ||
| 639 | output_buffer.append(TemplateList()); | 607 | output_buffer.append(TemplateList()); |
| 640 | } | 608 | } |
| 641 | 609 | ||
| 642 | - QList< QFuture<void> > futures; | ||
| 643 | - futures.reserve(src.size()); | 610 | + QFutureSynchronizer<void> futures; |
| 644 | for (int i=0; i<src.size(); i++) { | 611 | for (int i=0; i<src.size(); i++) { |
| 645 | input_buffer[i].append(src[i]); | 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 | for (int i=0; i<src.size(); i++) dst.append(output_buffer[i]); | 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 | private: | 631 | private: |
| 660 | 632 |
sdk/plugins/normalize.cpp
| @@ -14,6 +14,7 @@ | @@ -14,6 +14,7 @@ | ||
| 14 | * limitations under the License. * | 14 | * limitations under the License. * |
| 15 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | 15 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ |
| 16 | 16 | ||
| 17 | +#include <QFutureSynchronizer> | ||
| 17 | #include <QtConcurrentRun> | 18 | #include <QtConcurrentRun> |
| 18 | #include <opencv2/imgproc/imgproc.hpp> | 19 | #include <opencv2/imgproc/imgproc.hpp> |
| 19 | #include <opencv2/highgui/highgui.hpp> | 20 | #include <opencv2/highgui/highgui.hpp> |
| @@ -22,6 +23,7 @@ | @@ -22,6 +23,7 @@ | ||
| 22 | 23 | ||
| 23 | #include "core/common.h" | 24 | #include "core/common.h" |
| 24 | #include "core/opencvutils.h" | 25 | #include "core/opencvutils.h" |
| 26 | +#include "core/qtutils.h" | ||
| 25 | 27 | ||
| 26 | using namespace cv; | 28 | using namespace cv; |
| 27 | 29 | ||
| @@ -119,18 +121,16 @@ private: | @@ -119,18 +121,16 @@ private: | ||
| 119 | bv.push_back(Mat(1, dims, CV_64FC1)); | 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 | const bool parallel = (data.size() > 1000) && Globals->parallelism; | 125 | const bool parallel = (data.size() > 1000) && Globals->parallelism; |
| 124 | - | ||
| 125 | for (size_t c = 0; c < mv.size(); c++) { | 126 | for (size_t c = 0; c < mv.size(); c++) { |
| 126 | for (int i=0; i<dims; i++) | 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 | av[c] = av[c].reshape(1, data.first().m().rows); | 130 | av[c] = av[c].reshape(1, data.first().m().rows); |
| 130 | bv[c] = bv[c].reshape(1, data.first().m().rows); | 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 | merge(av, a); | 135 | merge(av, a); |
| 136 | merge(bv, b); | 136 | merge(bv, b); |
| @@ -224,6 +224,30 @@ class RowWiseMeanCenterTransform : public Transform | @@ -224,6 +224,30 @@ class RowWiseMeanCenterTransform : public Transform | ||
| 224 | 224 | ||
| 225 | BR_REGISTER(Transform, RowWiseMeanCenterTransform) | 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 | } // namespace br | 251 | } // namespace br |
| 228 | 252 | ||
| 229 | #include "normalize.moc" | 253 | #include "normalize.moc" |
sdk/plugins/output.cpp
| @@ -176,7 +176,7 @@ class rrOutput : public MatrixOutput | @@ -176,7 +176,7 @@ class rrOutput : public MatrixOutput | ||
| 176 | if (!byLine) files.append(queryFiles[i]); | 176 | if (!byLine) files.append(queryFiles[i]); |
| 177 | 177 | ||
| 178 | typedef QPair<float,int> Pair; | 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 | if (pair.first < threshold) break; | 180 | if (pair.first < threshold) break; |
| 181 | File target = targetFiles[pair.second]; | 181 | File target = targetFiles[pair.second]; |
| 182 | target.set("Score", QString::number(pair.first)); | 182 | target.set("Score", QString::number(pair.first)); |
| @@ -278,7 +278,7 @@ class rankOutput : public MatrixOutput | @@ -278,7 +278,7 @@ class rankOutput : public MatrixOutput | ||
| 278 | for (int i=0; i<queryFiles.size(); i++) { | 278 | for (int i=0; i<queryFiles.size(); i++) { |
| 279 | typedef QPair<float,int> Pair; | 279 | typedef QPair<float,int> Pair; |
| 280 | int rank = 1; | 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 | if(targetFiles[pair.second].get<QString>("Label") == queryFiles[i].get<QString>("Label")) { | 282 | if(targetFiles[pair.second].get<QString>("Label") == queryFiles[i].get<QString>("Label")) { |
| 283 | ranks.append(rank); | 283 | ranks.append(rank); |
| 284 | positions.append(pair.second); | 284 | positions.append(pair.second); |
sdk/plugins/quantize.cpp
| @@ -14,6 +14,8 @@ | @@ -14,6 +14,8 @@ | ||
| 14 | * limitations under the License. * | 14 | * limitations under the License. * |
| 15 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | 15 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ |
| 16 | 16 | ||
| 17 | +#include <QFutureSynchronizer> | ||
| 18 | +#include <QtConcurrentRun> | ||
| 17 | #include <openbr_plugin.h> | 19 | #include <openbr_plugin.h> |
| 18 | 20 | ||
| 19 | #include "core/opencvutils.h" | 21 | #include "core/opencvutils.h" |
| @@ -109,6 +111,145 @@ class PackTransform : public UntrainableTransform | @@ -109,6 +111,145 @@ class PackTransform : public UntrainableTransform | ||
| 109 | 111 | ||
| 110 | BR_REGISTER(Transform, PackTransform) | 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 | } // namespace br | 253 | } // namespace br |
| 113 | 254 | ||
| 114 | #include "quantize.moc" | 255 | #include "quantize.moc" |
sdk/plugins/regions.cpp
| @@ -120,6 +120,7 @@ class MergeTransform : public UntrainableMetaTransform | @@ -120,6 +120,7 @@ class MergeTransform : public UntrainableMetaTransform | ||
| 120 | 120 | ||
| 121 | void project(const Template &src, Template &dst) const | 121 | void project(const Template &src, Template &dst) const |
| 122 | { | 122 | { |
| 123 | + dst.file = src.file; | ||
| 123 | std::vector<Mat> mv; | 124 | std::vector<Mat> mv; |
| 124 | foreach (const Mat &m, src) | 125 | foreach (const Mat &m, src) |
| 125 | mv.push_back(m); | 126 | mv.push_back(m); |
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 | #include <QtConcurrentRun> | 2 | #include <QtConcurrentRun> |
| 2 | #include <openbr_plugin.h> | 3 | #include <openbr_plugin.h> |
| 3 | 4 | ||
| @@ -34,16 +35,16 @@ class CrossValidateTransform : public MetaTransform | @@ -34,16 +35,16 @@ class CrossValidateTransform : public MetaTransform | ||
| 34 | return; | 35 | return; |
| 35 | } | 36 | } |
| 36 | 37 | ||
| 37 | - QList< QFuture<void> > futures; | 38 | + QFutureSynchronizer<void> futures; |
| 38 | for (int i=0; i<numPartitions; i++) { | 39 | for (int i=0; i<numPartitions; i++) { |
| 39 | TemplateList partitionedData = data; | 40 | TemplateList partitionedData = data; |
| 40 | for (int j=partitionedData.size()-1; j>=0; j--) | 41 | for (int j=partitionedData.size()-1; j>=0; j--) |
| 41 | if (partitions[j] == i) | 42 | if (partitions[j] == i) |
| 42 | partitionedData.removeAt(j); | 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 | void project(const Template &src, Template &dst) const | 50 | void project(const Template &src, Template &dst) const |
sdk/plugins/youtube.cpp
share/openbr/openbr.bib
| @@ -33,7 +33,6 @@ | @@ -33,7 +33,6 @@ | ||
| 33 | Author = {Lacey S. Best-Rowden}, | 33 | Author = {Lacey S. Best-Rowden}, |
| 34 | Howpublished = {https://github.com/lbestrowden}, | 34 | Howpublished = {https://github.com/lbestrowden}, |
| 35 | Title = {bestrow1 at msu.edu}} | 35 | Title = {bestrow1 at msu.edu}} |
| 36 | - | ||
| 37 | 36 | ||
| 38 | % Software | 37 | % Software |
| 39 | @misc{OpenBR, | 38 | @misc{OpenBR, |
| @@ -87,6 +86,14 @@ | @@ -87,6 +86,14 @@ | ||
| 87 | 86 | ||
| 88 | 87 | ||
| 89 | % Papers | 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 | @inproceedings{belhumeur11, | 97 | @inproceedings{belhumeur11, |
| 91 | Author = {Belhumeur, P.N. and Jacobs, D.W. and Kriegman, D.J. and Kumar, N.}, | 98 | Author = {Belhumeur, P.N. and Jacobs, D.W. and Kriegman, D.J. and Kumar, N.}, |
| 92 | Booktitle = {Computer Vision and Pattern Recognition (CVPR), 2011 IEEE Conference on}, | 99 | Booktitle = {Computer Vision and Pattern Recognition (CVPR), 2011 IEEE Conference on}, |