Commit 8748d391e60b3c50556cbe2f5f45b5c6473a657d

Authored by Scott Klum
2 parents 6bceaac2 89c438fe

Merge branch 'master' of https://github.com/biometrics/openbr

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
1 #!/bin/bash 1 #!/bin/bash
2 2
3 -ALGORITHM=OpenBR 3 +ALGORITHM=FaceRecognitionHoG
4 4
5 if [ ! -f evalFaceRecognition-MEDS.sh ]; then 5 if [ ! -f evalFaceRecognition-MEDS.sh ]; then
6 echo "Run this script from the scripts folder!" 6 echo "Run this script from the scripts folder!"
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 &amp;targetInput, const QString &amp;queryInput, const @@ -236,7 +236,7 @@ void BEE::makeMask(const QString &amp;targetInput, const QString &amp;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&lt;Mat&gt; &amp;src) @@ -143,6 +143,7 @@ Mat OpenCVUtils::toMatByRow(const QList&lt;Mat&gt; &amp;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 &amp;m, int r, int c) @@ -172,22 +173,6 @@ QString OpenCVUtils::elemToString(const Mat &amp;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 &amp;m) @@ -224,18 +209,6 @@ QStringList OpenCVUtils::matrixToStringList(const Mat &amp;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 &amp;gallery) @@ -385,6 +390,15 @@ TemplateList TemplateList::fromGallery(const br::File &amp;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 &amp;conte @@ -904,11 +930,8 @@ void br::Context::messageHandler(QtMsgType type, const QMessageLogContext &amp;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 &amp;conte @@ -917,8 +940,6 @@ void br::Context::messageHandler(QtMsgType type, const QMessageLogContext &amp;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 &amp;dst, TemplateList &amp;src) const @@ -1293,12 +1321,11 @@ void Transform::backProject(const TemplateList &amp;dst, TemplateList &amp;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 &amp;target, const TemplateList &amp;query, Ou @@ -1326,16 +1353,16 @@ void Distance::compare(const TemplateList &amp;target, const TemplateList &amp;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
  1 +if(NOT ${BR_EMBEDDED})
  2 + set(BR_THIRDPARTY_SRC ${BR_THIRDPARTY_SRC} plugins/gui.cpp)
  3 +endif()
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
  1 +#include <QCoreApplication>
1 #include <QProcess> 2 #include <QProcess>
2 #include <openbr_plugin.h> 3 #include <openbr_plugin.h>
3 4
1 -Subproject commit 5dcad735fc16ce86937f992df8d4ba274dc81c14 1 +Subproject commit c11ff0f0bb451fb5576bd4b8122c32278e52042a
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},