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 4  
5 5 add_executable(br br.cpp ${BR_RESOURCES})
6 6 target_link_libraries(br openbr ${CMAKE_THREAD_LIBS_INIT})
  7 +qt5_use_modules(br ${QT_DEPENDENCIES})
  8 +
7 9 install(TARGETS br RUNTIME DESTINATION bin)
8 10  
9 11 add_test(NAME br_initialize WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND br)
... ...
app/br/br.cpp
... ... @@ -14,6 +14,9 @@
14 14 * limitations under the License. *
15 15 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16 16  
  17 +#include <QCoreApplication>
  18 +#include <QRunnable>
  19 +#include <QThreadPool>
17 20 #include <stdio.h>
18 21 #include <stdlib.h>
19 22 #include <string.h>
... ... @@ -47,171 +50,193 @@
47 50 * \endcode
48 51 */
49 52  
50   -static void help()
  53 +class FakeMain : public QRunnable
51 54 {
52   - printf("<arg> = Input; {arg} = Output; [arg] = Optional; (arg0|...|argN) = Choice\n"
53   - "\n"
54   - "==== Core Commands ====\n"
55   - "-train <gallery> ... <gallery> [{model}]\n"
56   - "-enroll <input_gallery> ... <input_gallery> {output_gallery}\n"
57   - "-compare <target_gallery> <query_gallery> [{output}]\n"
58   - "-eval <simmat> <mask> [{csv}]\n"
59   - "-plot <file> ... <file> {destination}\n"
60   - "\n"
61   - "==== Other Commands ====\n"
62   - "-fuse <simmat> ... <simmat> <mask> (None|MinMax|ZScore|WScore) (Min|Max|Sum[W1:W2:...:Wn]|Replace|Difference|None) {simmat}\n"
63   - "-cluster <simmat> ... <simmat> <aggressiveness> {csv}\n"
64   - "-makeMask <target_gallery> <query_gallery> {mask}\n"
65   - "-combineMasks <mask> ... <mask> {mask} (And|Or)\n"
66   - "-cat <gallery> ... <gallery> {gallery}\n"
67   - "-convert <template> {template}\n"
68   - "-reformat <target_sigset> <query_sigset> <simmat> {output}\n"
69   - "-evalClassification <predicted_gallery> <truth_gallery>\n"
70   - "-evalRegression <predicted_gallery> <truth_gallery>\n"
71   - "-evalClusters <clusters> <sigset>\n"
72   - "-confusion <file> <score>\n"
73   - "-plotMetadata <file> ... <file> <columns>\n"
74   - "\n"
75   - "==== Configuration ====\n"
76   - "-<key> <value>\n"
77   - "\n"
78   - "==== Miscellaneous ====\n"
79   - "-objects [abstraction [implementation]]\n"
80   - "-about\n"
81   - "-version\n"
82   - "-shell\n"
83   - "-exit\n");
84   -}
  55 + int argc;
  56 + char **argv;
85 57  
86   -static void check(bool condition, const char *error_message)
87   -{
88   - if (!condition) {
89   - printf("%s\n", error_message);
90   - br_finalize();
91   - exit(1);
92   - }
93   -}
  58 +public:
  59 + FakeMain(int argc_, char **argv_)
  60 + : argc(argc_), argv(argv_) {}
94 61  
95   -int main(int argc, char *argv[])
96   -{
97   - br_initialize(argc, argv);
  62 + void run()
  63 + {
  64 + // Remove program name
  65 + argv = &argv[1];
  66 + argc--;
98 67  
99   - // Remove program name
100   - argv = &argv[1];
101   - argc--;
102   -
103   - if (argc == 0) printf("%s\nTry running 'br -help'\n", br_about());
104   -
105   - bool shell = false;
106   - while (shell || (argc > 0)) {
107   - const char *fun;
108   - int parc;
109   - const char **parv;
110   - if (shell) {
111   - printf("> ");
112   - br_read_line(&parc, &parv);
113   - if (parc == 0) continue;
114   - fun = parv[0];
115   - parc--;
116   - parv = &parv[1];
117   - } else /* argc > 0 */ {
118   - fun = &argv[0][1];
119   - parc = 0; while ((1+parc < argc) && (argv[1+parc][0] != '-')) parc++;
120   - parv = (const char **)&argv[1];
121   - argc = argc - (1+parc);
122   - argv = &argv[parc+1];
123   - }
  68 + if (argc == 0) printf("%s\nTry running 'br -help'\n", br_about());
124 69  
125   - // Core Tasks
126   - if (!strcmp(fun, "train")) {
127   - check(parc >= 1, "Insufficient parameter count for 'train'.");
128   - br_train_n(parc == 1 ? 1 : parc-1, parv, parc == 1 ? "" : parv[parc-1]);
129   - } else if (!strcmp(fun, "enroll")) {
130   - check(parc >= 1, "Insufficient parameter count for 'enroll'.");
131   - if (parc == 1) br_enroll(parv[0]);
132   - else br_enroll_n(parc-1, parv, parv[parc-1]);
133   - } else if (!strcmp(fun, "compare")) {
134   - check((parc >= 2) && (parc <= 3), "Incorrect parameter count for 'compare'.");
135   - br_compare(parv[0], parv[1], parc == 3 ? parv[2] : "");
136   - } else if (!strcmp(fun, "eval")) {
137   - check((parc >= 2) && (parc <= 3), "Incorrect parameter count for 'eval'.");
138   - br_eval(parv[0], parv[1], parc == 3 ? parv[2] : "");
139   - } else if (!strcmp(fun, "plot")) {
140   - check(parc >= 2, "Incorrect parameter count for 'plot'.");
141   - br_plot(parc-1, parv, parv[parc-1], true);
142   - }
  70 + bool shell = false;
  71 + while (shell || (argc > 0)) {
  72 + const char *fun;
  73 + int parc;
  74 + const char **parv;
  75 + if (shell) {
  76 + printf("> ");
  77 + br_read_line(&parc, &parv);
  78 + if (parc == 0) continue;
  79 + fun = parv[0];
  80 + parc--;
  81 + parv = &parv[1];
  82 + } else /* argc > 0 */ {
  83 + fun = &argv[0][1];
  84 + parc = 0; while ((1+parc < argc) && (argv[1+parc][0] != '-')) parc++;
  85 + parv = (const char **)&argv[1];
  86 + argc = argc - (1+parc);
  87 + argv = &argv[parc+1];
  88 + }
  89 +
  90 + // Core Tasks
  91 + if (!strcmp(fun, "train")) {
  92 + check(parc >= 1, "Insufficient parameter count for 'train'.");
  93 + br_train_n(parc == 1 ? 1 : parc-1, parv, parc == 1 ? "" : parv[parc-1]);
  94 + } else if (!strcmp(fun, "enroll")) {
  95 + check(parc >= 1, "Insufficient parameter count for 'enroll'.");
  96 + if (parc == 1) br_enroll(parv[0]);
  97 + else br_enroll_n(parc-1, parv, parv[parc-1]);
  98 + } else if (!strcmp(fun, "compare")) {
  99 + check((parc >= 2) && (parc <= 3), "Incorrect parameter count for 'compare'.");
  100 + br_compare(parv[0], parv[1], parc == 3 ? parv[2] : "");
  101 + } else if (!strcmp(fun, "eval")) {
  102 + check((parc >= 2) && (parc <= 3), "Incorrect parameter count for 'eval'.");
  103 + br_eval(parv[0], parv[1], parc == 3 ? parv[2] : "");
  104 + } else if (!strcmp(fun, "plot")) {
  105 + check(parc >= 2, "Incorrect parameter count for 'plot'.");
  106 + br_plot(parc-1, parv, parv[parc-1], true);
  107 + }
143 108  
144   - // Secondary Tasks
145   - else if (!strcmp(fun, "fuse")) {
146   - check(parc >= 5, "Insufficient parameter count for 'fuse'.");
147   - br_fuse(parc-4, parv, parv[parc-4], parv[parc-3], parv[parc-2], parv[parc-1]);
148   - } else if (!strcmp(fun, "cluster")) {
149   - check(parc >= 3, "Insufficient parameter count for 'cluster'.");
150   - br_cluster(parc-2, parv, atof(parv[parc-2]), parv[parc-1]);
151   - } else if (!strcmp(fun, "makeMask")) {
152   - check(parc == 3, "Incorrect parameter count for 'makeMask'.");
153   - br_make_mask(parv[0], parv[1], parv[2]);
154   - } else if (!strcmp(fun, "combineMasks")) {
155   - check(parc >= 4, "Insufficient parameter count for 'combineMasks'.");
156   - br_combine_masks(parc-2, parv, parv[parc-2], parv[parc-1]);
157   - } else if (!strcmp(fun, "cat")) {
158   - check(parc >= 2, "Insufficient parameter count for 'cat'.");
159   - br_cat(parc-1, parv, parv[parc-1]);
160   - } else if (!strcmp(fun, "convert")) {
161   - check(parc == 2, "Incorrect parameter count for 'convert'.");
162   - br_convert(parv[0], parv[1]);
163   - } else if (!strcmp(fun, "reformat")) {
164   - check(parc == 4, "Incorrect parameter count for 'reformat'.");
165   - br_reformat(parv[0], parv[1], parv[2], parv[3]);
166   - } else if (!strcmp(fun, "evalClassification")) {
167   - check(parc == 2, "Incorrect parameter count for 'evalClassification'.");
168   - br_eval_classification(parv[0], parv[1]);
169   - } else if (!strcmp(fun, "evalRegression")) {
170   - check(parc == 2, "Incorrect parameter count for 'evalRegression'.");
171   - br_eval_regression(parv[0], parv[1]);
172   - } else if (!strcmp(fun, "evalClusters")) {
173   - check(parc == 2, "Incorrect parameter count for 'evalClusters'.");
174   - br_eval_clustering(parv[0], parv[1]);
175   - } else if (!strcmp(fun, "confusion")) {
176   - check(parc == 2, "Incorrect parameter count for 'confusion'.");
177   - int true_positives, false_positives, true_negatives, false_negatives;
178   - br_confusion(parv[0], atof(parv[1]),
179   - &true_positives, &false_positives, &true_negatives, &false_negatives);
180   - printf("True Positives = %d\nFalse Positives = %d\nTrue Negatives = %d\nFalseNegatives = %d\n",
181   - true_positives, false_positives, true_negatives, false_negatives);
182   - } else if (!strcmp(fun, "plotMetadata")) {
183   - check(parc >= 2, "Incorrect parameter count for 'plotMetadata'.");
184   - br_plot_metadata(parc-1, parv, parv[parc-1], true);
  109 + // Secondary Tasks
  110 + else if (!strcmp(fun, "fuse")) {
  111 + check(parc >= 5, "Insufficient parameter count for 'fuse'.");
  112 + br_fuse(parc-4, parv, parv[parc-4], parv[parc-3], parv[parc-2], parv[parc-1]);
  113 + } else if (!strcmp(fun, "cluster")) {
  114 + check(parc >= 3, "Insufficient parameter count for 'cluster'.");
  115 + br_cluster(parc-2, parv, atof(parv[parc-2]), parv[parc-1]);
  116 + } else if (!strcmp(fun, "makeMask")) {
  117 + check(parc == 3, "Incorrect parameter count for 'makeMask'.");
  118 + br_make_mask(parv[0], parv[1], parv[2]);
  119 + } else if (!strcmp(fun, "combineMasks")) {
  120 + check(parc >= 4, "Insufficient parameter count for 'combineMasks'.");
  121 + br_combine_masks(parc-2, parv, parv[parc-2], parv[parc-1]);
  122 + } else if (!strcmp(fun, "cat")) {
  123 + check(parc >= 2, "Insufficient parameter count for 'cat'.");
  124 + br_cat(parc-1, parv, parv[parc-1]);
  125 + } else if (!strcmp(fun, "convert")) {
  126 + check(parc == 2, "Incorrect parameter count for 'convert'.");
  127 + br_convert(parv[0], parv[1]);
  128 + } else if (!strcmp(fun, "reformat")) {
  129 + check(parc == 4, "Incorrect parameter count for 'reformat'.");
  130 + br_reformat(parv[0], parv[1], parv[2], parv[3]);
  131 + } else if (!strcmp(fun, "evalClassification")) {
  132 + check(parc == 2, "Incorrect parameter count for 'evalClassification'.");
  133 + br_eval_classification(parv[0], parv[1]);
  134 + } else if (!strcmp(fun, "evalRegression")) {
  135 + check(parc == 2, "Incorrect parameter count for 'evalRegression'.");
  136 + br_eval_regression(parv[0], parv[1]);
  137 + } else if (!strcmp(fun, "evalClusters")) {
  138 + check(parc == 2, "Incorrect parameter count for 'evalClusters'.");
  139 + br_eval_clustering(parv[0], parv[1]);
  140 + } else if (!strcmp(fun, "confusion")) {
  141 + check(parc == 2, "Incorrect parameter count for 'confusion'.");
  142 + int true_positives, false_positives, true_negatives, false_negatives;
  143 + br_confusion(parv[0], atof(parv[1]),
  144 + &true_positives, &false_positives, &true_negatives, &false_negatives);
  145 + printf("True Positives = %d\nFalse Positives = %d\nTrue Negatives = %d\nFalseNegatives = %d\n",
  146 + true_positives, false_positives, true_negatives, false_negatives);
  147 + } else if (!strcmp(fun, "plotMetadata")) {
  148 + check(parc >= 2, "Incorrect parameter count for 'plotMetadata'.");
  149 + br_plot_metadata(parc-1, parv, parv[parc-1], true);
  150 + }
  151 +
  152 + // Miscellaneous
  153 + else if (!strcmp(fun, "help")) {
  154 + check(parc == 0, "No parameters expected for 'help'.");
  155 + help();
  156 + } else if (!strcmp(fun, "objects")) {
  157 + check(parc <= 2, "Incorrect parameter count for 'objects'.");
  158 + printf("%s\n", br_objects(parc >= 1 ? parv[0] : ".*", parc >= 2 ? parv[1] : ".*"));
  159 + } else if (!strcmp(fun, "about")) {
  160 + check(parc == 0, "No parameters expected for 'about'.");
  161 + printf("%s\n", br_about());
  162 + } else if (!strcmp(fun, "version")) {
  163 + check(parc == 0, "No parameters expected for 'version'.");
  164 + printf("%s\n", br_version());
  165 + } else if (!strcmp(fun, "shell")) {
  166 + check(parc == 0, "No parameters expected for 'shell'.");
  167 + shell = true;
  168 + } else if (!strcmp(fun, "exit")) {
  169 + check(parc == 0, "No parameters expected for 'exit'.");
  170 + shell = false;
  171 + } else if (!strcmp(fun, "br")) {
  172 + printf("That's me!\n");
  173 + } else if (parc <= 1) {
  174 + br_set_property(fun, parc >=1 ? parv[0] : "");
  175 + } else {
  176 + printf("Unrecognized function '%s'\n", fun);
  177 + }
185 178 }
186 179  
187   - // Miscellaneous
188   - else if (!strcmp(fun, "help")) {
189   - check(parc == 0, "No parameters expected for 'help'.");
190   - help();
191   - } else if (!strcmp(fun, "objects")) {
192   - check(parc <= 2, "Incorrect parameter count for 'objects'.");
193   - printf("%s\n", br_objects(parc >= 1 ? parv[0] : ".*", parc >= 2 ? parv[1] : ".*"));
194   - } else if (!strcmp(fun, "about")) {
195   - check(parc == 0, "No parameters expected for 'about'.");
196   - printf("%s\n", br_about());
197   - } else if (!strcmp(fun, "version")) {
198   - check(parc == 0, "No parameters expected for 'version'.");
199   - printf("%s\n", br_version());
200   - } else if (!strcmp(fun, "shell")) {
201   - check(parc == 0, "No parameters expected for 'shell'.");
202   - shell = true;
203   - } else if (!strcmp(fun, "exit")) {
204   - check(parc == 0, "No parameters expected for 'exit'.");
205   - shell = false;
206   - } else if (!strcmp(fun, "br")) {
207   - printf("That's me!\n");
208   - } else if (parc <= 1) {
209   - br_set_property(fun, parc >=1 ? parv[0] : "");
210   - } else {
211   - printf("Unrecognized function '%s'\n", fun);
  180 + QCoreApplication::exit();
  181 + }
  182 +
  183 +private:
  184 + static void check(bool condition, const char *error_message)
  185 + {
  186 + if (!condition) {
  187 + printf("%s\n", error_message);
  188 + QCoreApplication::exit();
212 189 }
213 190 }
214 191  
  192 + static void help()
  193 + {
  194 + printf("<arg> = Input; {arg} = Output; [arg] = Optional; (arg0|...|argN) = Choice\n"
  195 + "\n"
  196 + "==== Core Commands ====\n"
  197 + "-train <gallery> ... <gallery> [{model}]\n"
  198 + "-enroll <input_gallery> ... <input_gallery> {output_gallery}\n"
  199 + "-compare <target_gallery> <query_gallery> [{output}]\n"
  200 + "-eval <simmat> <mask> [{csv}]\n"
  201 + "-plot <file> ... <file> {destination}\n"
  202 + "\n"
  203 + "==== Other Commands ====\n"
  204 + "-fuse <simmat> ... <simmat> <mask> (None|MinMax|ZScore|WScore) (Min|Max|Sum[W1:W2:...:Wn]|Replace|Difference|None) {simmat}\n"
  205 + "-cluster <simmat> ... <simmat> <aggressiveness> {csv}\n"
  206 + "-makeMask <target_gallery> <query_gallery> {mask}\n"
  207 + "-combineMasks <mask> ... <mask> {mask} (And|Or)\n"
  208 + "-cat <gallery> ... <gallery> {gallery}\n"
  209 + "-convert <template> {template}\n"
  210 + "-reformat <target_sigset> <query_sigset> <simmat> {output}\n"
  211 + "-evalClassification <predicted_gallery> <truth_gallery>\n"
  212 + "-evalRegression <predicted_gallery> <truth_gallery>\n"
  213 + "-evalClusters <clusters> <sigset>\n"
  214 + "-confusion <file> <score>\n"
  215 + "-plotMetadata <file> ... <file> <columns>\n"
  216 + "\n"
  217 + "==== Configuration ====\n"
  218 + "-<key> <value>\n"
  219 + "\n"
  220 + "==== Miscellaneous ====\n"
  221 + "-objects [abstraction [implementation]]\n"
  222 + "-about\n"
  223 + "-version\n"
  224 + "-shell\n"
  225 + "-exit\n");
  226 + }
  227 +};
  228 +
  229 +int main(int argc, char *argv[])
  230 +{
  231 + br_initialize(argc, argv);
  232 +
  233 + // Do argument execution in another thread so this main thread can run an event loop.
  234 + // When adding fakeMain to the global thread pool we give it a higher (slower) priority
  235 + // so that it is preempted when parallel work is available,
  236 + // otherwise we would only achieve (n-1)/n CPU utilization.
  237 + FakeMain *fakeMain = new FakeMain(argc, argv);
  238 + QThreadPool::globalInstance()->start(fakeMain, 1);
  239 + QCoreApplication::exec();
  240 +
215 241 br_finalize();
216   - return 0;
217 242 }
... ...
scripts/evalFaceRecognition-MEDS.sh
1 1 #!/bin/bash
2 2  
3   -ALGORITHM=OpenBR
  3 +ALGORITHM=FaceRecognitionHoG
4 4  
5 5 if [ ! -f evalFaceRecognition-MEDS.sh ]; then
6 6 echo "Run this script from the scripts folder!"
... ...
scripts/trainAgeRegression-PCSO.sh
... ... @@ -4,8 +4,8 @@ if [ ! -f trainAgeRegression-PCSO.sh ]; then
4 4 exit
5 5 fi
6 6  
7   -#rm -f ../models/features/FaceClassificationRegistration
8   -#rm -f ../models/features/FaceClassificationExtraction
9   -rm -f ../models/algorithms/AgeRegression
  7 +#rm -f ../share/openbr/models/features/FaceClassificationRegistration
  8 +#rm -f ../share/openbr/models/features/FaceClassificationExtraction
  9 +rm -f ../share/openbr/models/algorithms/AgeRegression
10 10  
11 11 br -algorithm AgeRegression -path ../data/PCSO/Images -train "../data/PCSO/PCSO.db[query='SELECT File,Age,PersonID FROM PCSO WHERE Age >= 15 AND AGE <= 75', subset=0:200]" ../share/openbr/models/algorithms/AgeRegression
... ...
scripts/trainFaceRecognition-PCSO.sh
... ... @@ -4,10 +4,10 @@ if [ ! -f trainFaceRecognition-PCSO.sh ]; then
4 4 exit
5 5 fi
6 6  
7   -#rm -f ../models/features/FaceRecognitionRegistration
8   -#rm -f ../models/features/FaceRecognitionExtraction
9   -#rm -f ../models/features/FaceRecognitionEmbedding
10   -#rm -f ../models/features/FaceRecognitionQuantization
11   -rm -f ../models/algorithms/FaceRecognition
  7 +#rm -f ../share/openbr/models/features/FaceRecognitionRegistration
  8 +#rm -f ../share/openbr/models/features/FaceRecognitionExtraction
  9 +#rm -f ../share/openbr/models/features/FaceRecognitionEmbedding
  10 +#rm -f ../share/openbr/models/features/FaceRecognitionQuantization
  11 +rm -f ../share/openbr/models/algorithms/FaceRecognition
12 12  
13 13 br -algorithm FaceRecognition -path ../data/PCSO/Images -train "../data/PCSO/PCSO.db[query='SELECT File,'S'||PersonID,PersonID FROM PCSO', subset=0:5:6000]" ../share/openbr/models/algorithms/FaceRecognition
... ...
scripts/trainGenderClassification-PCSO.sh
... ... @@ -4,8 +4,8 @@ if [ ! -f trainGenderClassification-PCSO.sh ]; then
4 4 exit
5 5 fi
6 6  
7   -#rm -f ../models/features/FaceClassificationRegistration
8   -#rm -f ../models/features/FaceClassificationExtraction
9   -rm -f ../models/algorithms/GenderClassification
  7 +#rm -f ../share/openbr/models/features/FaceClassificationRegistration
  8 +#rm -f ../share/openbr/models/features/FaceClassificationExtraction
  9 +rm -f ../share/openbr/models/algorithms/GenderClassification
10 10  
11 11 br -algorithm GenderClassification -path ../data/PCSO/Images -train "../data/PCSO/PCSO.db[query='SELECT File,Gender,PersonID FROM PCSO', subset=0:8000]" ../share/openbr/models/algorithms/GenderClassification
... ...
scripts/trainImageRetrieval-LFW.sh 0 โ†’ 100755
  1 +#!/bin/bash
  2 +if [ ! -f trainImageRetrieval-LFW.sh ]; then
  3 + echo "Run this script from the scripts folder!"
  4 + exit
  5 +fi
  6 +
  7 +if [ ! -e Algorithm_Dataset ]; then
  8 + mkdir Algorithm_Dataset
  9 +fi
  10 +
  11 +if [ ! -e LFW.mask ]; then
  12 + br -makeMask ~/lfw2[step=10] . LFW.mask
  13 +fi
  14 +
  15 +rm -f ../share/openbr/models/algorithms/ImageRetrieval
  16 +
  17 +br -algorithm ImageRetrieval -train ~/lfw2[step=10] ../share/openbr/models/algorithms/ImageRetrieval
  18 +br -algorithm ImageRetrieval -compare ~/lfw2[step=10] . ImageRetrieval_LFW.mtx -eval ImageRetrieval_LFW.mtx LFW.mask Algorithm_Dataset/ImageRetrieval_LFW.csv
  19 +br -plot Algorithm_Dataset/*_LFW.csv LFW
  20 +
  21 +#rm -f ../share/openbr/models/algorithms/ImageRetrieval
... ...
sdk/core/bee.cpp
... ... @@ -236,7 +236,7 @@ void BEE::makeMask(const QString &amp;targetInput, const QString &amp;queryInput, const
236 236 qDebug("Making mask from %s and %s to %s", qPrintable(targetInput), qPrintable(queryInput), qPrintable(mask));
237 237  
238 238 FileList targetFiles = TemplateList::fromGallery(targetInput).files();
239   - FileList queryFiles = TemplateList::fromGallery(queryInput).files();
  239 + FileList queryFiles = (queryInput == ".") ? targetFiles : TemplateList::fromGallery(queryInput).files();
240 240 QList<float> targetLabels = targetFiles.labels();
241 241 QList<float> queryLabels = queryFiles.labels();
242 242 QList<int> targetPartitions = targetFiles.crossValidationPartitions();
... ...
sdk/core/opencvutils.cpp
... ... @@ -143,6 +143,7 @@ Mat OpenCVUtils::toMatByRow(const QList&lt;Mat&gt; &amp;src)
143 143  
144 144 int rows = 0; foreach (const Mat &m, src) rows += m.rows;
145 145 int cols = src.first().cols;
  146 + if (cols == 0) qFatal("Columnless matrix!");
146 147 int type = src.first().type();
147 148 Mat dst(rows, cols, type);
148 149  
... ... @@ -172,22 +173,6 @@ QString OpenCVUtils::elemToString(const Mat &amp;m, int r, int c)
172 173 return "?";
173 174 }
174 175  
175   -float OpenCVUtils::elemToFloat(const Mat &m, int r, int c)
176   -{
177   - assert(m.channels() == 1);
178   - switch (m.depth()) {
179   - case CV_8U: return float(m.at<quint8>(r,c));
180   - case CV_8S: return float(m.at<qint8>(r,c));
181   - case CV_16U: return float(m.at<quint16>(r,c));
182   - case CV_16S: return float(m.at<qint16>(r,c));
183   - case CV_32S: return float(m.at<qint32>(r,c));
184   - case CV_32F: return float(m.at<float>(r,c));
185   - case CV_64F: return float(m.at<double>(r,c));
186   - default: qFatal("Unknown matrix depth");
187   - }
188   - return 0;
189   -}
190   -
191 176 QString OpenCVUtils::matrixToString(const Mat &m)
192 177 {
193 178 QString result;
... ... @@ -224,18 +209,6 @@ QStringList OpenCVUtils::matrixToStringList(const Mat &amp;m)
224 209 return results;
225 210 }
226 211  
227   -QList<float> OpenCVUtils::matrixToVector(const Mat &m)
228   -{
229   - QList<float> results;
230   - vector<Mat> mv;
231   - split(m, mv);
232   - foreach (const Mat &mc, mv)
233   - for (int i=0; i<mc.rows; i++)
234   - for (int j=0; j<mc.cols; j++)
235   - results.append(elemToFloat(mc, i, j));
236   - return results;
237   -}
238   -
239 212 Point2f OpenCVUtils::toPoint(const QPointF &qPoint)
240 213 {
241 214 return Point2f(qPoint.x(), qPoint.y());
... ...
sdk/core/opencvutils.h
... ... @@ -40,10 +40,38 @@ namespace OpenCVUtils
40 40  
41 41 // From image
42 42 QString elemToString(const cv::Mat &m, int r, int c);
43   - float elemToFloat(const cv::Mat &m, int r, int c);
44 43 QString matrixToString(const cv::Mat &m);
45 44 QStringList matrixToStringList(const cv::Mat &m);
46   - QList<float> matrixToVector(const cv::Mat &m);
  45 +
  46 + template <typename T>
  47 + T getElement(const cv::Mat &m, int r, int c)
  48 + {
  49 + assert(m.channels() == 1);
  50 + switch (m.depth()) {
  51 + case CV_8U: return T(m.at<quint8>(r,c));
  52 + case CV_8S: return T(m.at<qint8>(r,c));
  53 + case CV_16U: return T(m.at<quint16>(r,c));
  54 + case CV_16S: return T(m.at<qint16>(r,c));
  55 + case CV_32S: return T(m.at<qint32>(r,c));
  56 + case CV_32F: return T(m.at<float>(r,c));
  57 + case CV_64F: return T(m.at<double>(r,c));
  58 + default: qFatal("Unknown matrix depth!");
  59 + }
  60 + return 0;
  61 + }
  62 +
  63 + template <typename T>
  64 + QList<T> matrixToVector(const cv::Mat &m)
  65 + {
  66 + QList<T> results;
  67 + std::vector<cv::Mat> mv;
  68 + cv::split(m, mv);
  69 + foreach (const cv::Mat &mc, mv)
  70 + for (int i=0; i<mc.rows; i++)
  71 + for (int j=0; j<mc.cols; j++)
  72 + results.append(getElement<float>(mc, i, j));
  73 + return results;
  74 + }
47 75  
48 76 // Conversions
49 77 cv::Point2f toPoint(const QPointF &qPoint);
... ...
sdk/core/qtutils.cpp
... ... @@ -39,7 +39,7 @@ QStringList QtUtils::getFiles(QDir dir, bool recursive)
39 39  
40 40 QStringList files;
41 41 foreach (const QString &file, NaturalStringSort(dir.entryList(QDir::Files)))
42   - files.append(QDir::cleanPath(dir.absoluteFilePath(file)));
  42 + files.append(dir.absoluteFilePath(file));
43 43  
44 44 if (!recursive) return files;
45 45  
... ...
sdk/core/qtutils.h
... ... @@ -21,6 +21,7 @@
21 21 #include <QDir>
22 22 #include <QFile>
23 23 #include <QFileInfo>
  24 +#include <QFuture>
24 25 #include <QMap>
25 26 #include <QString>
26 27 #include <QStringList>
... ...
sdk/frvt2012.cpp
... ... @@ -29,7 +29,12 @@ static const int frvt2012_template_size = 768;
29 29  
30 30 static void initialize(const string &configuration_location)
31 31 {
32   - if (Globals == NULL) Context::initialize(0, NULL, QString::fromStdString(configuration_location));
  32 + // Fake the command line arguments
  33 + int argc = 1;
  34 + char arg1[1]; arg1[0]='\0';
  35 + char *argv[] = { arg1 };
  36 +
  37 + if (Globals == NULL) Context::initialize(argc, argv, QString::fromStdString(configuration_location));
33 38 Globals->quiet = true;
34 39 Globals->parallelism = 0;
35 40 }
... ...
sdk/openbr.cpp
... ... @@ -103,7 +103,7 @@ void br_fuse(int num_input_simmats, const char *input_simmats[], const char *mas
103 103 Fuse(QtUtils::toStringList(num_input_simmats, input_simmats), mask, normalization, fusion, output_simmat);
104 104 }
105 105  
106   -void br_initialize(int argc, char *argv[], const char *sdk_path)
  106 +void br_initialize(int &argc, char *argv[], const char *sdk_path)
107 107 {
108 108 Context::initialize(argc, argv, sdk_path);
109 109 }
... ...
sdk/openbr.h
... ... @@ -211,7 +211,7 @@ BR_EXPORT void br_fuse(int num_input_simmats, const char *input_simmats[], const
211 211 * \brief Wraps br::Context::initialize()
212 212 * \see br_initialize_qt br_finalize
213 213 */
214   -BR_EXPORT void br_initialize(int argc, char *argv[], const char *sdk_path = "");
  214 +BR_EXPORT void br_initialize(int &argc, char *argv[], const char *sdk_path = "");
215 215  
216 216 /*!
217 217 * \brief Wraps br::Context::initializeQt()
... ...
sdk/openbr_export.cpp
... ... @@ -82,6 +82,7 @@ $ br -help
82 82 * -# Consider the free open source program <a href="http://wincdemu.sysprogs.org">WinCDEmu</a> if you need a program to mount ISO images.
83 83 * -# You will have to register with Microsoft after installation, but it's free.
84 84 * -# Grab any available <a href="http://www.microsoft.com/visualstudio/eng/downloads#d-visual-studio-2012-update">Visual Studio Updates</a>.
  85 + * -# Download and install <a href="http://msdn.microsoft.com/en-us/windows/hardware/hh852363.aspx">Windows 8 SDK</a>.
85 86 * -# <a href="http://www.cmake.org/files/v2.8/cmake-2.8.10.2-win32-x86.exe">Download CMake 2.8.10.2</a> and install.
86 87 * -# During installation setup select "add CMake to PATH".
87 88 * -# <a href="http://downloads.sourceforge.net/project/opencvlibrary/opencv-unix/2.4.4/OpenCV-2.4.4.tar.bz2">Download OpenCV 2.4.4</a>.
... ... @@ -100,19 +101,8 @@ $ br -help
100 101 * $ nmake install
101 102 * $ nmake clean
102 103 * \endcode
103   - * -# <a href="http://releases.qt-project.org/qt5/5.0.1/single/qt-everywhere-opensource-src-5.0.1.zip">Download Qt 5.0.1</a> and unzip.
104   - * -# Install Perl/Python/Ruby dependencies as explained in the "Windows" section of "README". Make sure they are added to "path" when given the option during installation.
  104 + * -# <a href="http://releases.qt-project.org/digia/5.0.1/backups/2013-01-18-412/qt-windows-opensource-5.0.1-msvc2012_64-x86_64-offline-2013-01-18-412.exe">Download Qt 5.0.1</a> and install.
105 105 * -# <a href="http://www.microsoft.com/en-us/download/confirmation.aspx?id=6812">Download Direct X Software Developement Kit</a> and install.
106   - * -# From the VS2012 x64 Cross Tools Command Prompt:
107   - * \code
108   - * $ cd qt-everywhere-opensource-src-5.0.1
109   - * $ configure -prefix C:\Qt\5.0.1\msvc2012 -opensource -confirm-license -nomake examples -nomake tests
110   - * $ nmake
111   - * $ nmake install
112   - * $ cd ..
113   - * $ rmdir /Q /S qt-everywhere-opensource-src-5.0.1
114   - * \endcode
115   - * -# nmake will take several hours to finish.
116 106 * -# Create a <a href="github.com">GitHub</a> account and follow their instructions for <a href="https://help.github.com/articles/set-up-git">setting up Git</a>.
117 107 * -# Launch "Git Bash" from the Desktop and clone OpenBR:
118 108 * \code
... ... @@ -128,7 +118,7 @@ $ br -help
128 118 * $ cd C:\openbr
129 119 * $ mkdir build-msvc2012
130 120 * $ cd build-msvc2012
131   - * $ cmake -G "CodeBlocks - NMake Makefiles" -DCMAKE_PREFIX_PATH="C:/OpenCV-2.4.4/build-msvc2012/install;C:/Qt/5.0.1/msvc2012" -DCMAKE_INSTALL_PREFIX="./install" -DBR_INSTALL_DEPENDENCIES=ON -DCMAKE_BUILD_TYPE=Release ..
  121 + * $ cmake -G "CodeBlocks - NMake Makefiles" -DCMAKE_PREFIX_PATH="C:/OpenCV-2.4.4/build-msvc2012/install;C:/Qt/Qt5.0.1/5.0.1/msvc2012_64" -DCMAKE_INSTALL_PREFIX="./install" -DBR_INSTALL_DEPENDENCIES=ON -DCMAKE_BUILD_TYPE=Release ..
132 122 * $ nmake
133 123 * $ nmake install
134 124 * \endcode
... ... @@ -147,7 +137,7 @@ $ br -help
147 137 * -# Browse to your prexisting build directory "C:\openbr\build-msvc2012" then select "Next".
148 138 * -# Clear any text in the "arguments" box then select "Run CMake" then "Finish".
149 139 * -# You're all set! You can find more information on Qt Creator <a href="http://qt-project.org/doc/qtcreator-2.6/">here</a> if you need.
150   - * -# Package OpenBR!
  140 + * -# (Optional) Package OpenBR!
151 141 * -# <a href="http://sourceforge.net/projects/nsis/files/NSIS%202/2.46/nsis-2.46-setup.exe/download?use_mirror=iweb&download=">Download NSIS 2.46</a> and install.
152 142 * -# From the VS2012 x64 Cross Tools Command Prompt:
153 143 * \code
... ... @@ -226,7 +216,7 @@ $ br -help
226 216 * -# Browse to your prexisting build directory "C:\openbr\build-mingw64" then select "Next".
227 217 * -# Clear any text in the "arguments" box then select "Run CMake" then "Finish".
228 218 * -# You're all set! You can find more information on Qt Creator <a href="http://qt-project.org/doc/qtcreator-2.6/">here</a> if you need.
229   - * -# Package OpenBR!
  219 + * -# (Optional) Package OpenBR!
230 220 * -# <a href="http://sourceforge.net/projects/nsis/files/NSIS%202/2.46/nsis-2.46-setup.exe/download?use_mirror=iweb&download=">Download NSIS 2.46</a> and install.
231 221 * -# From the MinGW-w64 Command Prompt:
232 222 * \code
... ... @@ -277,7 +267,7 @@ $ br -help
277 267 * $ cd build
278 268 * $ cmake -DCMAKE_PREFIX_PATH=~/Qt5.0.1/5.0.1/clang_64 -DCMAKE_BUILD_TYPE=Release ..
279 269 * $ make -j4
280   - * $ make install
  270 + * $ sudo make install
281 271 * \endcode
282 272 * -# Hack OpenBR!
283 273 * -# Open Qt Creator IDE
... ... @@ -289,12 +279,12 @@ $ br -help
289 279 * -# Browse to your prexisting build directory "~/openbr/build" then select "Continue".
290 280 * -# Select "Run CMake" then "Done".
291 281 * -# You're all set! You can find more information on Qt Creator <a href="http://qt-project.org/doc/qtcreator">here</a> if you need.
292   - * -# Package OpenBR!
  282 + * -# (Optional) Package OpenBR!
293 283 * \code
294 284 * $ cd openbr/build
295 285 * $ make package
296 286 * \endcode
297   - * -# Build OpenBR documentation!
  287 + * -# (Optional) Build OpenBR documentation!
298 288 * -# <a href="ftp://ftp.stack.nl/pub/users/dimitri/doxygen-1.8.2.src.tar.gz">Download Doxygen 1.8.2</a> and install:
299 289 * \code
300 290 * $ cd ~/Downloads
... ... @@ -367,7 +357,7 @@ $ br -help
367 357 * $ cd build
368 358 * $ cmake -DCMAKE_PREFIX_PATH=~/Qt5.0.1/5.0.1/gcc_64 -DCMAKE_BUILD_TYPE=Release ..
369 359 * $ make -j4
370   - * $ make install
  360 + * $ sudo make install
371 361 * \endcode
372 362 * -# Hack OpenBR!
373 363 * -# Open Qt Creator IDE
... ... @@ -379,7 +369,7 @@ $ br -help
379 369 * -# Browse to your prexisting build directory "~/openbr/build" then select "Next".
380 370 * -# Select "Run CMake" then "Finish".
381 371 * -# You're all set! You can find more information on Qt Creator <a href="http://qt-project.org/doc/qtcreator-2.6/">here</a> if you need.
382   - * -# Package OpenBR!
  372 + * -# (Optional) Package OpenBR!
383 373 * \code
384 374 * $ cd openbr/build
385 375 * $ make package
... ... @@ -434,7 +424,7 @@ $ br -help
434 424 * $ cd build-icc
435 425 * $ cmake -DCMAKE_C_COMPILER=/opt/intel/bin/icc -DCMAKE_CXX_COMPILER=/opt/intel/bin/icpc -DCMAKE_PREFIX_PATH=~/Qt5.0.1/5.0.1/gcc_64 -DCMAKE_BUILD_TYPE=Release ..
436 426 * $ make -j4
437   - * $ make install
  427 + * $ sudo make install
438 428 * \endcode
439 429 * -# Hack OpenBR!
440 430 * -# Open Qt Creator IDE
... ... @@ -446,7 +436,7 @@ $ br -help
446 436 * -# Browse to your prexisting build directory "~/openbr/build" then select "Next".
447 437 * -# Select "Run CMake" then "Finish".
448 438 * -# You're all set! You can find more information on Qt Creator <a href="http://qt-project.org/doc/qtcreator-2.6/">here</a> if you need.
449   - * -# Package OpenBR!
  439 + * -# (Optional) Package OpenBR!
450 440 * \code
451 441 * $ cd openbr/build
452 442 * $ make package
... ...
sdk/openbr_plugin.cpp
... ... @@ -14,6 +14,7 @@
14 14 * limitations under the License. *
15 15 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16 16  
  17 +#include <QFutureSynchronizer>
17 18 #include <QMetaProperty>
18 19 #include <QPointF>
19 20 #include <QRect>
... ... @@ -28,6 +29,10 @@
28 29 #include <iostream>
29 30 #include <openbr_plugin.h>
30 31  
  32 +#ifndef BR_EMBEDDED
  33 +#include <QApplication>
  34 +#endif
  35 +
31 36 #include "version.h"
32 37 #include "core/bee.h"
33 38 #include "core/common.h"
... ... @@ -385,6 +390,15 @@ TemplateList TemplateList::fromGallery(const br::File &amp;gallery)
385 390 QScopedPointer<Gallery> i(Gallery::make(file));
386 391 TemplateList newTemplates = i->read();
387 392 newTemplates = newTemplates.mid(gallery.get<int>("pos", 0), gallery.get<int>("length", -1));
  393 +
  394 + const int step = gallery.get<int>("step", 1);
  395 + if (step > 1) {
  396 + TemplateList downsampled; downsampled.reserve(newTemplates.size()/step);
  397 + for (int i=0; i<newTemplates.size(); i+=step)
  398 + downsampled.append(newTemplates[i]);
  399 + newTemplates = downsampled;
  400 + }
  401 +
388 402 if (gallery.get<bool>("reduce", false)) newTemplates = newTemplates.reduced();
389 403 const int crossValidate = gallery.get<int>("crossValidate");
390 404 if (crossValidate > 0) srand(0);
... ... @@ -770,27 +784,31 @@ int br::Context::timeRemaining() const
770 784 return std::max(0, int((1 - p) / p * startTime.elapsed())) / 1000;
771 785 }
772 786  
773   -void br::Context::trackFutures(QList< QFuture<void> > &futures)
774   -{
775   - foreach (QFuture<void> future, futures) {
776   - QCoreApplication::processEvents();
777   - future.waitForFinished();
778   - }
779   -}
780   -
781 787 bool br::Context::checkSDKPath(const QString &sdkPath)
782 788 {
783 789 return QFileInfo(sdkPath + "/share/openbr/openbr.bib").exists();
784 790 }
785 791  
786   -void br::Context::initialize(int argc, char *argv[], const QString &sdkPath)
  792 +void br::Context::initialize(int &argc, char *argv[], const QString &sdkPath)
787 793 {
  794 + // We take in argc as a reference due to:
  795 + // https://bugreports.qt-project.org/browse/QTBUG-5637
  796 + // QApplication should be initialized before anything else.
  797 + // Since we can't ensure that it gets deleted last, we never delete it.
  798 + static QCoreApplication *application = NULL;
  799 + if (application == NULL) {
  800 +#ifndef BR_EMBEDDED
  801 + application = new QApplication(argc, argv);
  802 +#else
  803 + application = new QCoreApplication(argc, argv);
  804 +#endif
  805 + }
  806 +
788 807 if (Globals == NULL) {
789 808 Globals = new Context();
790 809 Globals->init(File());
791 810 }
792 811  
793   - Globals->coreApplication = QSharedPointer<QCoreApplication>(new QCoreApplication(argc, argv));
794 812 initializeQt(sdkPath);
795 813  
796 814 #ifdef BR_DISTRIBUTED
... ... @@ -853,7 +871,10 @@ void br::Context::initializeQt(QString sdkPath)
853 871  
854 872 void br::Context::finalize()
855 873 {
856   - // Trigger registerd finalizers
  874 + // Is anyone still running?
  875 + QThreadPool::globalInstance()->waitForDone();
  876 +
  877 + // Trigger registered finalizers
857 878 QList< QSharedPointer<Initializer> > initializers = Factory<Initializer>::makeAll();
858 879 foreach (const QSharedPointer<Initializer> &initializer, initializers)
859 880 initializer->finalize();
... ... @@ -883,6 +904,11 @@ QString br::Context::scratchPath()
883 904  
884 905 void br::Context::messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
885 906 {
  907 + // Something about this method is not thread safe, and will lead to crashes if qDebug
  908 + // statements are called from multiple threads. Unless we lock the whole thing...
  909 + static QMutex generalLock;
  910 + QMutexLocker locker(&generalLock);
  911 +
886 912 QString txt;
887 913 switch (type) {
888 914 case QtDebugMsg:
... ... @@ -904,11 +930,8 @@ void br::Context::messageHandler(QtMsgType type, const QMessageLogContext &amp;conte
904 930 Globals->mostRecentMessage = txt;
905 931  
906 932 if (Globals->logFile.isWritable()) {
907   - static QMutex logLock;
908   - logLock.lock();
909 933 Globals->logFile.write(qPrintable(txt));
910 934 Globals->logFile.flush();
911   - logLock.unlock();
912 935 }
913 936  
914 937 if (type == QtFatalMsg) {
... ... @@ -917,8 +940,6 @@ void br::Context::messageHandler(QtMsgType type, const QMessageLogContext &amp;conte
917 940 Globals->finalize();
918 941 abort();
919 942 }
920   -
921   - QCoreApplication::processEvents(); // Used to retrieve messages before event loop starts
922 943 }
923 944  
924 945 Context *br::Globals = NULL;
... ... @@ -1147,14 +1168,12 @@ private:
1147 1168 for (int i=0; i<templatesList.size(); i++)
1148 1169 templatesList[i] = Downsample(templatesList[i], transforms[i]);
1149 1170  
1150   - QList< QFuture<void> > futures;
1151   - const bool threaded = Globals->parallelism && (templatesList.size() > 1);
  1171 + QFutureSynchronizer<void> futures;
1152 1172 for (int i=0; i<templatesList.size(); i++) {
1153   - if (threaded) futures.append(QtConcurrent::run(_train, transforms[i], &templatesList[i]));
1154   - else _train (transforms[i], &templatesList[i]);
  1173 + if (Globals->parallelism) futures.addFuture(QtConcurrent::run(_train, transforms[i], &templatesList[i]));
  1174 + else _train (transforms[i], &templatesList[i]);
1155 1175 }
1156   -
1157   - if (threaded) Globals->trackFutures(futures);
  1176 + futures.waitForFinished();
1158 1177 }
1159 1178  
1160 1179 void project(const Template &src, Template &dst) const
... ... @@ -1250,42 +1269,51 @@ Transform *Transform::clone() const
1250 1269 return clone;
1251 1270 }
1252 1271  
1253   -static void _backProject(const Transform *transform, const Template *dst, Template *src)
  1272 +static void _project(const Transform *transform, const Template *src, Template *dst)
1254 1273 {
1255 1274 try {
1256   - transform->backProject(*dst, *src);
  1275 + transform->project(*src, *dst);
1257 1276 } catch (...) {
1258 1277 qWarning("Exception triggered when processing %s with transform %s", qPrintable(src->file.flat()), qPrintable(transform->objectName()));
1259   - *src = Template(dst->file);
1260   - src->file.set("FTE", true);
  1278 + *dst = Template(src->file);
  1279 + dst->file.set("FTE", true);
1261 1280 }
1262 1281 }
1263 1282  
1264   -
1265   -
1266   -// Default project(TemplateList) -- call project(template) separately for each input
1267   -// template
  1283 +// Default project(TemplateList) calls project(Template) separately for each element
1268 1284 void Transform::project(const TemplateList &src, TemplateList &dst) const
1269 1285 {
1270   - dst.clear();
1271   -
1272   - // Project templates derived from a single image, default implementation: project each
1273   - // input template to an ouptut template individually.
1274   - foreach(const Template & src_template, src) {
1275   - dst.append(Template(src_template.file));
1276   - try {
1277   - project(src_template, dst.back());
1278   - } catch (...) {
1279   - qWarning("Exception triggered when processing %s with transform %s", qPrintable(src_template.file.flat()), qPrintable(objectName()));
1280   - dst.back() = Template(src_template.file);
1281   - dst.back().file.set("FTE", true);
  1286 + dst.reserve(src.size());
  1287 +
  1288 + // There are certain conditions where we should process the templates in serial,
  1289 + // but generally we'd prefer to process them in parallel.
  1290 + if ((src.size() < 2) ||
  1291 + (QThreadPool::globalInstance()->activeThreadCount() >= QThreadPool::globalInstance()->maxThreadCount()) ||
  1292 + (Globals->parallelism == 0)) {
  1293 +
  1294 + foreach (const Template &t, src) {
  1295 + dst.append(Template());
  1296 + _project(this, &t, &dst.last());
1282 1297 }
  1298 + } else {
  1299 + for (int i=0; i<src.size(); i++)
  1300 + dst.append(Template());
  1301 + QFutureSynchronizer<void> futures;
  1302 + for (int i=0; i<dst.size(); i++)
  1303 + futures.addFuture(QtConcurrent::run(_project, this, &src[i], &dst[i]));
  1304 + futures.waitForFinished();
1283 1305 }
1284 1306 }
1285 1307  
1286   -void Transform::backProject(const Template &dst, Template &src) const
  1308 +static void _backProject(const Transform *transform, const Template *dst, Template *src)
1287 1309 {
1288   - src = dst;
  1310 + try {
  1311 + transform->backProject(*dst, *src);
  1312 + } catch (...) {
  1313 + qWarning("Exception triggered when processing %s with transform %s", qPrintable(src->file.flat()), qPrintable(transform->objectName()));
  1314 + *src = Template(dst->file);
  1315 + src->file.set("FTE", true);
  1316 + }
1289 1317 }
1290 1318  
1291 1319 void Transform::backProject(const TemplateList &dst, TemplateList &src) const
... ... @@ -1293,12 +1321,11 @@ void Transform::backProject(const TemplateList &amp;dst, TemplateList &amp;src) const
1293 1321 src.reserve(dst.size());
1294 1322 for (int i=0; i<dst.size(); i++) src.append(Template());
1295 1323  
1296   - QList< QFuture<void> > futures;
1297   - if (Globals->parallelism) futures.reserve(dst.size());
  1324 + QFutureSynchronizer<void> futures;
1298 1325 for (int i=0; i<dst.size(); i++)
1299   - if (Globals->parallelism) futures.append(QtConcurrent::run(_backProject, this, &dst[i], &src[i]));
1300   - else _backProject (this, &dst[i], &src[i]);
1301   - if (Globals->parallelism) Globals->trackFutures(futures);
  1326 + if (Globals->parallelism) futures.addFuture(QtConcurrent::run(_backProject, this, &dst[i], &src[i]));
  1327 + else _backProject (this, &dst[i], &src[i]);
  1328 + futures.waitForFinished();
1302 1329 }
1303 1330  
1304 1331 /* Distance - public methods */
... ... @@ -1326,16 +1353,16 @@ void Distance::compare(const TemplateList &amp;target, const TemplateList &amp;query, Ou
1326 1353 const bool stepTarget = target.size() > query.size();
1327 1354 const int totalSize = std::max(target.size(), query.size());
1328 1355 int stepSize = ceil(float(totalSize) / float(std::max(1, abs(Globals->parallelism))));
1329   - QList< QFuture<void> > futures; futures.reserve(ceil(float(totalSize)/float(stepSize)));
  1356 + QFutureSynchronizer<void> futures;
1330 1357 for (int i=0; i<totalSize; i+=stepSize) {
1331 1358 const TemplateList &targets(stepTarget ? TemplateList(target.mid(i, stepSize)) : target);
1332 1359 const TemplateList &queries(stepTarget ? query : TemplateList(query.mid(i, stepSize)));
1333 1360 const int targetOffset = stepTarget ? i : 0;
1334 1361 const int queryOffset = stepTarget ? 0 : i;
1335   - if (Globals->parallelism) futures.append(QtConcurrent::run(this, &Distance::compareBlock, targets, queries, output, targetOffset, queryOffset));
1336   - else compareBlock (targets, queries, output, targetOffset, queryOffset);
  1362 + if (Globals->parallelism) futures.addFuture(QtConcurrent::run(this, &Distance::compareBlock, targets, queries, output, targetOffset, queryOffset));
  1363 + else compareBlock (targets, queries, output, targetOffset, queryOffset);
1337 1364 }
1338   - if (Globals->parallelism) Globals->trackFutures(futures);
  1365 + futures.waitForFinished();
1339 1366 }
1340 1367  
1341 1368 QList<float> Distance::compare(const TemplateList &targets, const Template &query) const
... ...
sdk/openbr_plugin.h
... ... @@ -17,7 +17,6 @@
17 17 #ifndef __OPENBR_PLUGIN_H
18 18 #define __OPENBR_PLUGIN_H
19 19  
20   -#include <QCoreApplication>
21 20 #include <QDataStream>
22 21 #include <QDebug>
23 22 #include <QDir>
... ... @@ -466,7 +465,6 @@ private:
466 465 class BR_EXPORT Context : public Object
467 466 {
468 467 Q_OBJECT
469   - QSharedPointer<QCoreApplication> coreApplication;
470 468 QFile logFile;
471 469  
472 470 public:
... ... @@ -624,12 +622,6 @@ public:
624 622 int timeRemaining() const;
625 623  
626 624 /*!
627   - * \brief Continues to print the progress of the futures until they are completed.
628   - * \param futures The list of futures to track.
629   - */
630   - void trackFutures(QList< QFuture<void> > &futures);
631   -
632   - /*!
633 625 * \brief Returns \c true if \em sdkPath is valid, \c false otherwise.
634 626 * \param sdkPath The path to <tt>share/openbr/openbr.bib</tt>
635 627 */
... ... @@ -658,7 +650,7 @@ public:
658 650 * \note <a href="http://qt-project.org/">Qt</a> users should instead call initializeQt().
659 651 * \see initializeQt finalize
660 652 */
661   - static void initialize(int argc, char *argv[], const QString &sdkPath = "");
  653 + static void initialize(int &argc, char *argv[], const QString &sdkPath = "");
662 654  
663 655 /*!
664 656 * \brief Alternative to initialize() for <a href="http://qt-project.org/">Qt</a> users.
... ... @@ -998,7 +990,7 @@ public:
998 990 virtual void train(const TemplateList &data) = 0; /*!< \brief Train the transform. */
999 991 virtual void project(const Template &src, Template &dst) const = 0; /*!< \brief Apply the transform. */
1000 992 virtual void project(const TemplateList &src, TemplateList &dst) const; /*!< \brief Apply the transform. */
1001   - virtual void backProject(const Template &dst, Template &src) const; /*!< \brief Invert the transform. */
  993 + virtual void backProject(const Template &dst, Template &src) const { src = dst; } /*!< \brief Invert the transform. */
1002 994 virtual void backProject(const TemplateList &dst, TemplateList &src) const; /*!< \brief Invert the transform. */
1003 995  
1004 996 /*!< \brief Apply the transform, may update the transform's internal state */
... ... @@ -1113,6 +1105,7 @@ class BR_EXPORT TimeVaryingTransform : public Transform
1113 1105 {
1114 1106 Q_OBJECT
1115 1107  
  1108 +public:
1116 1109 virtual bool timeVarying() const { return true; }
1117 1110  
1118 1111 virtual void project(const Template &src, Template &dst) const
... ... @@ -1127,6 +1120,23 @@ class BR_EXPORT TimeVaryingTransform : public Transform
1127 1120 (void) dst; (void) src;
1128 1121 }
1129 1122  
  1123 + // Get a compile failure if this isn't here to go along with the other
  1124 + // projectUpdate, no idea why
  1125 + virtual void projectUpdate(const Template & src, Template & dst)
  1126 + {
  1127 + (void) src; (void) dst;
  1128 + qFatal("do something useful");
  1129 + }
  1130 +
  1131 + virtual void projectUpdate(const TemplateList &src, TemplateList &dst)
  1132 + {
  1133 + foreach (const Template & src_part, src) {
  1134 + Template out;
  1135 + projectUpdate(src_part, out);
  1136 + dst.append(out);
  1137 + }
  1138 + }
  1139 +
1130 1140 protected:
1131 1141 TimeVaryingTransform(bool independent = true, bool trainable = true) : Transform(independent, trainable) {}
1132 1142 };
... ... @@ -1170,6 +1180,51 @@ protected:
1170 1180 UntrainableMetaTransform() : UntrainableTransform(false) {}
1171 1181 };
1172 1182  
  1183 +/*!
  1184 + * \brief A MetaTransform that aggregates some sub-transforms
  1185 + */
  1186 +class BR_EXPORT CompositeTransform : public TimeVaryingTransform
  1187 +{
  1188 + Q_OBJECT
  1189 +
  1190 +public:
  1191 + Q_PROPERTY(QList<br::Transform*> transforms READ get_transforms WRITE set_transforms RESET reset_transforms)
  1192 + BR_PROPERTY(QList<br::Transform*>, transforms, QList<br::Transform*>())
  1193 +
  1194 + virtual void project(const Template &src, Template &dst) const
  1195 + {
  1196 + if (timeVarying()) qFatal("No const project defined for time-varying transform");
  1197 + _project(src, dst);
  1198 + }
  1199 +
  1200 + virtual void project(const TemplateList &src, TemplateList &dst) const
  1201 + {
  1202 + if (timeVarying()) qFatal("No const project defined for time-varying transform");
  1203 + _project(src, dst);
  1204 + }
  1205 +
  1206 + bool timeVarying() const { return isTimeVarying; }
  1207 +
  1208 + void init()
  1209 + {
  1210 + isTimeVarying = false;
  1211 + foreach (const br::Transform *transform, transforms) {
  1212 + if (transform->timeVarying()) {
  1213 + isTimeVarying = true;
  1214 + break;
  1215 + }
  1216 + }
  1217 + }
  1218 +
  1219 +protected:
  1220 + bool isTimeVarying;
  1221 +
  1222 + virtual void _project(const Template & src, Template & dst) const = 0;
  1223 + virtual void _project(const TemplateList & src, TemplateList & dst) const = 0;
  1224 +
  1225 + CompositeTransform() : TimeVaryingTransform(false) {}
  1226 +};
  1227 +
1173 1228 /*! @}*/
1174 1229  
1175 1230 /*!
... ...
sdk/plugins/algorithms.cpp
... ... @@ -32,7 +32,6 @@ class AlgorithmsInitializer : public Initializer
32 32 {
33 33 // Face
34 34 Globals->abbreviations.insert("FaceRecognition", "FaceDetection!<FaceRecognitionRegistration>!<FaceRecognitionExtraction>+<FaceRecognitionEmbedding>+<FaceRecognitionQuantization>:MatchProbability(ByteL1)");
35   - Globals->abbreviations.insert("FaceRecognitionNoTraining", "FaceDetection!ASEFEyes+Affine(86,86,0.25,0.35)!Blur(1.1)+Gamma(0.2)+DoG(1,2)+ContrastEq(0.1,10)+Mask+LBP(1,2)+RectRegions(8,8,6,6)+Hist(59)+Cat:ChiSquared");
36 35 Globals->abbreviations.insert("GenderClassification", "FaceDetection!<FaceClassificationRegistration>!<FaceClassificationExtraction>+<GenderClassifier>+Discard");
37 36 Globals->abbreviations.insert("AgeRegression", "FaceDetection!<FaceClassificationRegistration>!<FaceClassificationExtraction>+<AgeRegressor>+Discard");
38 37 Globals->abbreviations.insert("FaceQuality", "Open!Cascade(FrontalFace)+ASEFEyes+Affine(64,64,0.25,0.35)+ImageQuality+Cvt(Gray)+DFFS+Discard");
... ... @@ -43,6 +42,7 @@ class AlgorithmsInitializer : public Initializer
43 42 Globals->abbreviations.insert("OpenBR", "FaceRecognition");
44 43 Globals->abbreviations.insert("GenderEstimation", "GenderClassification");
45 44 Globals->abbreviations.insert("AgeEstimation", "AgeRegression");
  45 + Globals->abbreviations.insert("FaceRecognitionHoG", "Open+Cvt(Gray)+Cascade(FrontalFace)+ASEFEyes+Affine(64,64,0.25,0.35)+Gradient+Bin(0,360,8,true)+Merge+Integral+IntegralSampler+RootNorm+ProductQuantization(2,true):ProductQuantization(true)");
46 46  
47 47 // Generic Image Processing
48 48 Globals->abbreviations.insert("SIFT", "Open+KeyPointDetector(SIFT)+KeyPointDescriptor(SIFT):KeyPointMatcher(BruteForce)");
... ... @@ -50,7 +50,7 @@ class AlgorithmsInitializer : public Initializer
50 50 Globals->abbreviations.insert("SmallSIFT", "Open+LimitSize(512)+KeyPointDetector(SIFT)+KeyPointDescriptor(SIFT):KeyPointMatcher(BruteForce)");
51 51 Globals->abbreviations.insert("SmallSURF", "Open+LimitSize(512)+KeyPointDetector(SURF)+KeyPointDescriptor(SURF):KeyPointMatcher(BruteForce)");
52 52 Globals->abbreviations.insert("ColorHist", "Open+LimitSize(512)!EnsureChannels(3)+SplitChannels+Hist(256,0,8)+Cat+Normalize(L1):L2");
53   - Globals->abbreviations.insert("IHH", "Open+(RG+MAdd(0.5))/(Cvt(Gray)+Gradient+Bin(0,360,8,true))+Merge+Integral+IntegralSampler+CvtFloat+WordWise(RowWisePCA(8)+RowWiseMeanCenter+Binarize,Identity):L2");
  53 + Globals->abbreviations.insert("ImageRetrieval", "Open+Cvt(Gray)+Cascade(FrontalFace)+ASEFEyes+Affine(88,88,0.25,0.35)+Gradient+Bin(0,360,8,true)+Merge+Integral+IntegralSampler+WordWise(RowWisePCA(8)+RowWiseMeanCenter+Binarize,RowWisePCA)+Sentence:SentenceSimilarity");
54 54  
55 55 // Hash
56 56 Globals->abbreviations.insert("FileName", "Name+Identity:Identical");
... ...
sdk/plugins/cvt.cpp
... ... @@ -181,12 +181,17 @@ class RGTransform : public UntrainableTransform
181 181 for (int i=0; i<m.rows; i++)
182 182 for (int j=0; j<m.cols; j++) {
183 183 Vec3b v = m.at<Vec3b>(i,j);
184   - uchar& b = v[0];
185   - uchar& g = v[1];
186   - uchar& r = v[2];
187   -
188   - R.at<uchar>(i, j) = saturate_cast<uchar>(255.0*r/(r+g+b));
189   - G.at<uchar>(i, j) = saturate_cast<uchar>(255.0*g/(r+g+b));
  184 + const int b = v[0];
  185 + const int g = v[1];
  186 + const int r = v[2];
  187 + const int sum = b + g + r;
  188 + if (sum > 0) {
  189 + R.at<uchar>(i, j) = saturate_cast<uchar>(255.0*r/(r+g+b));
  190 + G.at<uchar>(i, j) = saturate_cast<uchar>(255.0*g/(r+g+b));
  191 + } else {
  192 + R.at<uchar>(i, j) = 0;
  193 + G.at<uchar>(i, j) = 0;
  194 + }
190 195 }
191 196  
192 197 dst.append(R);
... ...
sdk/plugins/distance.cpp
... ... @@ -14,11 +14,13 @@
14 14 * limitations under the License. *
15 15 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16 16  
  17 +#include <QFutureSynchronizer>
17 18 #include <QtConcurrentRun>
18 19 #include <opencv2/imgproc/imgproc.hpp>
19 20 #include <openbr_plugin.h>
20 21  
21 22 #include "core/distance_sse.h"
  23 +#include "core/qtutils.h"
22 24  
23 25 using namespace cv;
24 26  
... ... @@ -154,11 +156,11 @@ class PipeDistance : public Distance
154 156  
155 157 void train(const TemplateList &data)
156 158 {
157   - QList< QFuture<void> > futures;
  159 + QFutureSynchronizer<void> futures;
158 160 foreach (br::Distance *distance, distances)
159   - if (Globals->parallelism) futures.append(QtConcurrent::run(distance, &Distance::train, data));
160   - else distance->train(data);
161   - Globals->trackFutures(futures);
  161 + if (Globals->parallelism) futures.addFuture(QtConcurrent::run(distance, &Distance::train, data));
  162 + else distance->train(data);
  163 + futures.waitForFinished();
162 164 }
163 165  
164 166 float compare(const Template &a, const Template &b) const
... ...
sdk/plugins/eigen3.cpp
... ... @@ -145,15 +145,15 @@ protected:
145 145  
146 146 if (keep < 1) {
147 147 // Keep eigenvectors that retain a certain energy percentage.
148   - double totalEnergy = allEVals.sum();
  148 + const double totalEnergy = allEVals.sum();
149 149 if (totalEnergy == 0) {
150 150 keep = 0;
151 151 } else {
152 152 double currentEnergy = 0;
153   - int i;
154   - for (i=1; i<=allEVals.rows(); i++) {
155   - currentEnergy += allEVals(allEVals.rows()-i);
156   - if (currentEnergy / totalEnergy >= keep) break;
  153 + int i=0;
  154 + while ((currentEnergy / totalEnergy < keep) && (i < allEVals.rows())) {
  155 + currentEnergy += allEVals(allEVals.rows()-(i+1));
  156 + i++;
157 157 }
158 158 keep = i - drop;
159 159 }
... ...
sdk/plugins/format.cpp
... ... @@ -242,12 +242,11 @@ class DefaultFormat : public Format
242 242  
243 243 void write(const Template &t) const
244 244 {
245   - if (t.size() != 1) {
  245 + if (t.size() > 1) {
246 246 videoFormat videoWriter;
247 247 videoWriter.file = file;
248 248 videoWriter.write(t);
249   - }
250   - else {
  249 + } else if (t.size() == 1) {
251 250 imwrite(file.name.toStdString(), t);
252 251 }
253 252 }
... ... @@ -565,7 +564,6 @@ class webcamFormat : public Format
565 564  
566 565 BR_REGISTER(Format, webcamFormat)
567 566  
568   -#ifndef BR_EMBEDDED
569 567 /*!
570 568 * \ingroup formats
571 569 * \brief Decodes images from Base64 xml
... ... @@ -578,13 +576,15 @@ class xmlFormat : public Format
578 576  
579 577 Template read() const
580 578 {
  579 + Template t;
  580 +
  581 +#ifndef BR_EMBEDDED
581 582 QDomDocument doc(file);
582 583 QFile f(file);
583 584 if (!f.open(QIODevice::ReadOnly)) qFatal("Unable to open %s for reading.", qPrintable(file.flat()));
584 585 if (!doc.setContent(&f)) qFatal("Unable to parse %s.", qPrintable(file.flat()));
585 586 f.close();
586 587  
587   - Template t;
588 588 QDomElement docElem = doc.documentElement();
589 589 QDomNode subject = docElem.firstChild();
590 590 while (!subject.isNull()) {
... ... @@ -620,6 +620,7 @@ class xmlFormat : public Format
620 620 if (current.month() < dob.month()) age--;
621 621 t.file.set("Age", age);
622 622 }
  623 +#endif // BR_EMBEDDED
623 624  
624 625 return t;
625 626 }
... ... @@ -632,7 +633,6 @@ class xmlFormat : public Format
632 633 };
633 634  
634 635 BR_REGISTER(Format, xmlFormat)
635   -#endif // BR_EMBEDDED
636 636  
637 637 } // namespace br
638 638  
... ...
sdk/plugins/gallery.cpp
... ... @@ -14,6 +14,7 @@
14 14 * limitations under the License. *
15 15 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16 16  
  17 +#include <QtConcurrentRun>
17 18 #ifndef BR_EMBEDDED
18 19 #include <QNetworkAccessManager>
19 20 #include <QNetworkReply>
... ... @@ -103,9 +104,14 @@ class EmptyGallery : public Gallery
103 104  
104 105 // Add immediate subfolders
105 106 QDir dir(file);
106   - foreach (const QString &folder, NaturalStringSort(dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)))
107   - foreach (const QString &file, QtUtils::getFiles(dir.absoluteFilePath(folder), true))
108   - templates.append(File(file, folder));
  107 + QList< QFuture<TemplateList> > futures;
  108 + foreach (const QString &folder, NaturalStringSort(dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot))) {
  109 + const QDir subdir = dir.absoluteFilePath(folder);
  110 + if (Globals->parallelism) futures.append(QtConcurrent::run(&EmptyGallery::getTemplates, subdir));
  111 + else templates.append(getTemplates(subdir));
  112 + }
  113 + foreach (const QFuture<TemplateList> &future, futures)
  114 + templates.append(future.result());
109 115  
110 116 // Add root folder
111 117 foreach (const QString &fileName, QtUtils::getFiles(file.name, false))
... ... @@ -130,6 +136,15 @@ class EmptyGallery : public Gallery
130 136 format->write(t);
131 137 }
132 138 }
  139 +
  140 + static TemplateList getTemplates(const QDir &dir)
  141 + {
  142 + const QStringList files = QtUtils::getFiles(dir, true);
  143 + TemplateList templates; templates.reserve(files.size());
  144 + foreach (const QString &file, files)
  145 + templates.append(File(file, dir.dirName()));
  146 + return templates;
  147 + }
133 148 };
134 149  
135 150 BR_REGISTER(Gallery, EmptyGallery)
... ... @@ -662,7 +677,7 @@ class googleGallery : public Gallery
662 677 QNetworkReply *reply = networkAccessManager.get(request);
663 678  
664 679 while (!reply->isFinished())
665   - QCoreApplication::processEvents();
  680 + QThread::yieldCurrentThread();
666 681  
667 682 QString data(reply->readAll());
668 683 delete reply;
... ...
sdk/plugins/gui.cmake 0 โ†’ 100644
  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 111 assert(m.channels() == 1);
112 112 dst = Mat(m.rows, m.cols, CV_32FC1);
113 113 typedef QPair<float,int> Tuple;
114   - QList<Tuple> tuples = Common::Sort(OpenCVUtils::matrixToVector(m));
  114 + QList<Tuple> tuples = Common::Sort(OpenCVUtils::matrixToVector<float>(m));
115 115  
116 116 float prevValue = 0;
117 117 int prevRank = 0;
... ...
sdk/plugins/integral.cpp
... ... @@ -28,7 +28,7 @@ BR_REGISTER(Transform, IntegralTransform)
28 28  
29 29 /*!
30 30 * \ingroup transforms
31   - * \brief Sliding window feature extraction from a multi-channel intergral image.
  31 + * \brief Sliding window feature extraction from a multi-channel integral image.
32 32 * \author Josh Klontz \cite jklontz
33 33 */
34 34 class IntegralSamplerTransform : public UntrainableTransform
... ... @@ -38,9 +38,9 @@ class IntegralSamplerTransform : public UntrainableTransform
38 38 Q_PROPERTY(float scaleFactor READ get_scaleFactor WRITE set_scaleFactor RESET reset_scaleFactor STORED false)
39 39 Q_PROPERTY(float stepFactor READ get_stepFactor WRITE set_stepFactor RESET reset_stepFactor STORED false)
40 40 Q_PROPERTY(int minSize READ get_minSize WRITE set_minSize RESET reset_minSize STORED false)
41   - BR_PROPERTY(int, scales, 4)
  41 + BR_PROPERTY(int, scales, 5)
42 42 BR_PROPERTY(float, scaleFactor, 1.5)
43   - BR_PROPERTY(float, stepFactor, 0.25)
  43 + BR_PROPERTY(float, stepFactor, 0.75)
44 44 BR_PROPERTY(int, minSize, 8)
45 45  
46 46 void project(const Template &src, Template &dst) const
... ... @@ -53,22 +53,23 @@ class IntegralSamplerTransform : public UntrainableTransform
53 53 const int rowStep = channels * m.cols;
54 54  
55 55 int descriptors = 0;
56   - int currentSize = min(m.rows, m.cols)-1;
  56 + float idealSize = min(m.rows, m.cols)-1;
57 57 for (int scale=0; scale<scales; scale++) {
58   - descriptors += int(1+(m.rows-currentSize)/(currentSize*stepFactor)) *
59   - int(1+(m.cols-currentSize)/(currentSize*stepFactor));
60   - currentSize /= scaleFactor;
61   - if (currentSize < minSize)
62   - break;
  58 + const int currentSize(idealSize);
  59 + descriptors += (1+(m.rows-currentSize-1)/int(idealSize*stepFactor)) *
  60 + (1+(m.cols-currentSize-1)/int(idealSize*stepFactor));
  61 + idealSize /= scaleFactor;
  62 + if (idealSize < minSize) break;
63 63 }
64 64 Mat n(descriptors, channels, CV_32FC1);
65 65  
66 66 const qint32 *dataIn = (qint32*)m.data;
67 67 float *dataOut = (float*)n.data;
68   - currentSize = min(m.rows, m.cols)-1;
  68 + idealSize = min(m.rows, m.cols)-1;
69 69 int index = 0;
70 70 for (int scale=0; scale<scales; scale++) {
71   - const int currentStep = currentSize * stepFactor;
  71 + const int currentSize(idealSize);
  72 + const int currentStep(idealSize*stepFactor);
72 73 for (int i=currentSize; i<m.rows; i+=currentStep) {
73 74 for (int j=currentSize; j<m.cols; j+=currentStep) {
74 75 InputDescriptor a(dataIn+((i-currentSize)*rowStep+(j-currentSize)*channels), channels, 1);
... ... @@ -80,10 +81,13 @@ class IntegralSamplerTransform : public UntrainableTransform
80 81 index++;
81 82 }
82 83 }
83   - currentSize /= scaleFactor;
84   - if (currentSize < minSize)
85   - break;
  84 + idealSize /= scaleFactor;
  85 + if (idealSize < minSize) break;
86 86 }
  87 +
  88 + if (descriptors != index)
  89 + qFatal("Allocated %d descriptors but computed %d.", descriptors, index);
  90 +
87 91 dst.m() = n;
88 92 }
89 93 };
... ... @@ -133,38 +137,51 @@ class WordWiseTransform : public Transform
133 137 Q_OBJECT
134 138 Q_PROPERTY(br::Transform* getWords READ get_getWords WRITE set_getWords RESET reset_getWords)
135 139 Q_PROPERTY(br::Transform* byWord READ get_byWord WRITE set_byWord RESET reset_byWord)
  140 + Q_PROPERTY(int numWords READ get_numWords WRITE set_numWords RESET reset_numWords)
136 141 BR_PROPERTY(br::Transform*, getWords, NULL)
137 142 BR_PROPERTY(br::Transform*, byWord, NULL)
  143 + BR_PROPERTY(int, numWords, 0)
138 144  
139 145 void train(const TemplateList &data)
140 146 {
141 147 getWords->train(data);
142   - TemplateList words;
143   - getWords->project(data, words);
  148 + TemplateList bins;
  149 + getWords->project(data, bins);
144 150  
145   - const int columns = data.first().m().cols;
146   - int numWords = 0;
147   - foreach (const Template &t, words) {
  151 + numWords = 0;
  152 + foreach (const Template &t, bins) {
148 153 double minVal, maxVal;
149 154 minMaxLoc(t, &minVal, &maxVal);
150 155 numWords = max(numWords, int(maxVal)+1);
151 156 }
152 157  
153   - QVector<int> wordCounts(numWords, 0);
154   - foreach (const Template &t, words) {
155   - const Mat &m = t.m();
156   - for (int i=0; i<m.rows; i++)
157   - wordCounts[m.at<uchar>(i,0)]++;
158   - }
159   -
160   - QVector<Mat> trainingWords(numWords);
161   - for (int i=0; i<numWords; i++)
162   - trainingWords[i] = Mat(wordCounts[i], columns, CV_8UC1);
  158 + TemplateList reworded; reworded.reserve(data.size());
  159 + foreach (const Template &t, data)
  160 + reworded.append(reword(t));
  161 + byWord->train(reworded);
163 162 }
164 163  
165 164 void project(const Template &src, Template &dst) const
166 165 {
167   - (void) src; (void) dst;
  166 + byWord->project(reword(src), dst);
  167 + }
  168 +
  169 + Template reword(const Template &src) const
  170 + {
  171 + Template words;
  172 + getWords->project(src, words);
  173 + QVector<int> wordCounts(numWords, 0);
  174 + for (int i=0; i<words.m().rows; i++)
  175 + wordCounts[words.m().at<uchar>(i,0)]++;
  176 + Template reworded(src.file); reworded.reserve(numWords);
  177 + for (int i=0; i<numWords; i++)
  178 + reworded.append(Mat(wordCounts[i], src.m().cols, src.m().type()));
  179 + QVector<int> indicies(numWords, 0);
  180 + for (int i=0; i<src.m().rows; i++) {
  181 + const int word = words.m().at<uchar>(i,0);
  182 + src.m().row(i).copyTo(reworded[word].row(indicies[word]++));
  183 + }
  184 + return reworded;
168 185 }
169 186 };
170 187  
... ...
sdk/plugins/meta.cpp
... ... @@ -14,6 +14,7 @@
14 14 * limitations under the License. *
15 15 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16 16  
  17 +#include <QFutureSynchronizer>
17 18 #include <QtConcurrentRun>
18 19 #include <openbr_plugin.h>
19 20  
... ... @@ -60,76 +61,6 @@ static void _train(Transform *transform, const TemplateList *data)
60 61 transform->train(*data);
61 62 }
62 63  
63   -// For handling progress feedback
64   -static int depth = 0;
65   -
66   -static void acquireStep()
67   -{
68   - if (depth == 0) {
69   - Globals->currentStep = 0;
70   - Globals->totalSteps = 1;
71   - }
72   - depth++;
73   -}
74   -
75   -static void releaseStep()
76   -{
77   - depth--;
78   - Globals->currentStep = floor(Globals->currentStep * pow(10.0, double(depth))) / pow(10.0, double(depth));
79   - if (depth == 0)
80   - Globals->totalSteps = 0;
81   -}
82   -
83   -static void incrementStep()
84   -{
85   - Globals->currentStep += 1.0 / pow(10.0, double(depth));
86   -}
87   -
88   -/*!
89   - * \brief A MetaTransform that aggregates some sub-transforms
90   - */
91   -class BR_EXPORT CompositeTransform : public TimeVaryingTransform
92   -{
93   - Q_OBJECT
94   -
95   -public:
96   - Q_PROPERTY(QList<br::Transform*> transforms READ get_transforms WRITE set_transforms RESET reset_transforms)
97   - BR_PROPERTY(QList<br::Transform*>, transforms, QList<br::Transform*>())
98   -
99   - virtual void project(const Template &src, Template &dst) const
100   - {
101   - if (timeVarying()) qFatal("No const project defined for time-varying transform");
102   - _project(src, dst);
103   - }
104   -
105   - virtual void project(const TemplateList &src, TemplateList &dst) const
106   - {
107   - if (timeVarying()) qFatal("No const project defined for time-varying transform");
108   - _project(src, dst);
109   - }
110   -
111   - bool timeVarying() const { return isTimeVarying; }
112   -
113   - void init()
114   - {
115   - isTimeVarying = false;
116   - foreach (const br::Transform *transform, transforms) {
117   - if (transform->timeVarying()) {
118   - isTimeVarying = true;
119   - break;
120   - }
121   - }
122   - }
123   -
124   -protected:
125   - bool isTimeVarying;
126   -
127   - virtual void _project(const Template & src, Template & dst) const = 0;
128   - virtual void _project(const TemplateList & src, TemplateList & dst) const = 0;
129   -
130   - CompositeTransform() : TimeVaryingTransform(false) {}
131   -};
132   -
133 64 /*!
134 65 * \ingroup Transforms
135 66 * \brief Transforms in series.
... ... @@ -146,16 +77,13 @@ class PipeTransform : public CompositeTransform
146 77  
147 78 void train(const TemplateList &data)
148 79 {
149   - acquireStep();
150   -
151 80 TemplateList copy(data);
152 81 for (int i=0; i<transforms.size(); i++) {
  82 + fprintf(stderr, "%s training... ", qPrintable(transforms[i]->objectName()));
153 83 transforms[i]->train(copy);
  84 + fprintf(stderr, "projecting...\n");
154 85 copy >> *transforms[i];
155   - incrementStep();
156 86 }
157   -
158   - releaseStep();
159 87 }
160 88  
161 89 void backProject(const Template &dst, Template &src) const
... ... @@ -284,6 +212,38 @@ BR_REGISTER(Transform, ExpandTransform)
284 212  
285 213 /*!
286 214 * \ingroup transforms
  215 + * \brief It's like the opposite of ExpandTransform, but not really
  216 + * \author Charles Otto \cite caotto
  217 + *
  218 + * Given a set of templatelists as input, concatenate them onto a single Template
  219 + */
  220 +class ContractTransform : public UntrainableMetaTransform
  221 +{
  222 + Q_OBJECT
  223 +
  224 + virtual void project(const TemplateList &src, TemplateList &dst) const
  225 + {
  226 + //dst = Expanded(src);
  227 + Template out;
  228 +
  229 + foreach (const Template & t, src) {
  230 + out.merge(t);
  231 + }
  232 + dst.clear();
  233 + dst.append(out);
  234 + }
  235 +
  236 + virtual void project(const Template & src, Template & dst) const
  237 + {
  238 + qFatal("this has gone bad");
  239 + (void) src; (void) dst;
  240 + }
  241 +};
  242 +
  243 +BR_REGISTER(Transform, ContractTransform)
  244 +
  245 +/*!
  246 + * \ingroup transforms
287 247 * \brief Transforms in parallel.
288 248 * \author Josh Klontz \cite jklontz
289 249 *
... ... @@ -297,13 +257,12 @@ class ForkTransform : public CompositeTransform
297 257  
298 258 void train(const TemplateList &data)
299 259 {
300   - QList< QFuture<void> > futures;
301   - const bool threaded = Globals->parallelism && (transforms.size() > 1);
  260 + QFutureSynchronizer<void> futures;
302 261 for (int i=0; i<transforms.size(); i++) {
303   - if (threaded) futures.append(QtConcurrent::run(_train, transforms[i], &data));
304   - else _train (transforms[i], &data);
  262 + if (Globals->parallelism) futures.addFuture(QtConcurrent::run(_train, transforms[i], &data));
  263 + else _train (transforms[i], &data);
305 264 }
306   - if (threaded) Globals->trackFutures(futures);
  265 + futures.waitForFinished();
307 266 }
308 267  
309 268 void backProject(const Template &dst, Template &src) const {Transform::backProject(dst, src);}
... ... @@ -626,6 +585,15 @@ public:
626 585 // Process the single elemnt templates in parallel if parallelism is enabled.
627 586 void project(const TemplateList &src, TemplateList &dst) const
628 587 {
  588 + // Little ugly, but if we own a timeVaryingTransform and this gets called
  589 + // cast off the const modifier and use projectUpdate. This allows us to
  590 + // act as a single point of entry.
  591 + if (transform->timeVarying())
  592 + {
  593 + DistributeTemplateTransform * non_const = (DistributeTemplateTransform *) this;
  594 + non_const->projectUpdate(src,dst);
  595 + return;
  596 + }
629 597 // Pre-allocate output for each template
630 598 QList<TemplateList> output_buffer;
631 599 output_buffer.reserve(src.size());
... ... @@ -639,22 +607,26 @@ public:
639 607 output_buffer.append(TemplateList());
640 608 }
641 609  
642   - QList< QFuture<void> > futures;
643   - futures.reserve(src.size());
  610 + QFutureSynchronizer<void> futures;
644 611 for (int i=0; i<src.size(); i++) {
645 612 input_buffer[i].append(src[i]);
646   - if (Globals->parallelism)
647   - futures.append(QtConcurrent::run(_projectList, transform, &input_buffer[i], &output_buffer[i]));
648   - else
649   - _projectList(transform, &input_buffer[i], &output_buffer[i]);
  613 + if (Globals->parallelism) futures.addFuture(QtConcurrent::run(_projectList, transform, &input_buffer[i], &output_buffer[i]));
  614 + else _projectList( transform, &input_buffer[i], &output_buffer[i]);
650 615 }
651   -
652   - if (Globals->parallelism)
653   - Globals->trackFutures(futures);
  616 + futures.waitForFinished();
654 617  
655 618 for (int i=0; i<src.size(); i++) dst.append(output_buffer[i]);
656 619 }
657 620  
  621 + void projectUpdate(const TemplateList &src, TemplateList &dst)
  622 + {
  623 + if (!transform->timeVarying()) {
  624 + this->project(src, dst);
  625 + return;
  626 + }
  627 + this->transform->projectUpdate(src, dst);
  628 + }
  629 +
658 630  
659 631 private:
660 632  
... ...
sdk/plugins/normalize.cpp
... ... @@ -14,6 +14,7 @@
14 14 * limitations under the License. *
15 15 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16 16  
  17 +#include <QFutureSynchronizer>
17 18 #include <QtConcurrentRun>
18 19 #include <opencv2/imgproc/imgproc.hpp>
19 20 #include <opencv2/highgui/highgui.hpp>
... ... @@ -22,6 +23,7 @@
22 23  
23 24 #include "core/common.h"
24 25 #include "core/opencvutils.h"
  26 +#include "core/qtutils.h"
25 27  
26 28 using namespace cv;
27 29  
... ... @@ -119,18 +121,16 @@ private:
119 121 bv.push_back(Mat(1, dims, CV_64FC1));
120 122 }
121 123  
122   - QList< QFuture<void> > futures; futures.reserve(dims);
  124 + QFutureSynchronizer<void> futures;
123 125 const bool parallel = (data.size() > 1000) && Globals->parallelism;
124   -
125 126 for (size_t c = 0; c < mv.size(); c++) {
126 127 for (int i=0; i<dims; i++)
127   - if (parallel) futures.append(QtConcurrent::run(_train, method, mv[c], &av[c], &bv[c], i));
128   - else _train (method, mv[c], &av[c], &bv[c], i);
  128 + if (parallel) futures.addFuture(QtConcurrent::run(_train, method, mv[c], &av[c], &bv[c], i));
  129 + else _train (method, mv[c], &av[c], &bv[c], i);
129 130 av[c] = av[c].reshape(1, data.first().m().rows);
130 131 bv[c] = bv[c].reshape(1, data.first().m().rows);
131 132 }
132   -
133   - if (parallel) Globals->trackFutures(futures);
  133 + futures.waitForFinished();
134 134  
135 135 merge(av, a);
136 136 merge(bv, b);
... ... @@ -224,6 +224,30 @@ class RowWiseMeanCenterTransform : public Transform
224 224  
225 225 BR_REGISTER(Transform, RowWiseMeanCenterTransform)
226 226  
  227 +/*!
  228 + * \ingroup transforms
  229 + * \brief dst=sqrt(norm_L1(src)) proposed as RootSIFT in \cite Arandjelovic12
  230 + * \author Josh Klontz \cite jklontz
  231 + */
  232 +class RootNormTransform : public UntrainableTransform
  233 +{
  234 + Q_OBJECT
  235 +
  236 + void project(const Template &src, Template &dst) const
  237 + {
  238 + const Mat &m = src;
  239 + dst.m() = Mat(m.rows, m.cols, m.type());
  240 + for (int i=0; i<m.rows; i++) {
  241 + Mat temp;
  242 + cv::normalize(m.row(i), temp, 1, 0, NORM_L1);
  243 + cv::sqrt(temp, temp);
  244 + temp.copyTo(dst.m().row(i));
  245 + }
  246 + }
  247 +};
  248 +
  249 +BR_REGISTER(Transform, RootNormTransform)
  250 +
227 251 } // namespace br
228 252  
229 253 #include "normalize.moc"
... ...
sdk/plugins/output.cpp
... ... @@ -176,7 +176,7 @@ class rrOutput : public MatrixOutput
176 176 if (!byLine) files.append(queryFiles[i]);
177 177  
178 178 typedef QPair<float,int> Pair;
179   - foreach (const Pair &pair, Common::Sort(OpenCVUtils::matrixToVector(data.row(i)), true, limit)) {
  179 + foreach (const Pair &pair, Common::Sort(OpenCVUtils::matrixToVector<float>(data.row(i)), true, limit)) {
180 180 if (pair.first < threshold) break;
181 181 File target = targetFiles[pair.second];
182 182 target.set("Score", QString::number(pair.first));
... ... @@ -278,7 +278,7 @@ class rankOutput : public MatrixOutput
278 278 for (int i=0; i<queryFiles.size(); i++) {
279 279 typedef QPair<float,int> Pair;
280 280 int rank = 1;
281   - foreach (const Pair &pair, Common::Sort(OpenCVUtils::matrixToVector(data.row(i)), true)) {
  281 + foreach (const Pair &pair, Common::Sort(OpenCVUtils::matrixToVector<float>(data.row(i)), true)) {
282 282 if(targetFiles[pair.second].get<QString>("Label") == queryFiles[i].get<QString>("Label")) {
283 283 ranks.append(rank);
284 284 positions.append(pair.second);
... ...
sdk/plugins/quantize.cpp
... ... @@ -14,6 +14,8 @@
14 14 * limitations under the License. *
15 15 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16 16  
  17 +#include <QFutureSynchronizer>
  18 +#include <QtConcurrentRun>
17 19 #include <openbr_plugin.h>
18 20  
19 21 #include "core/opencvutils.h"
... ... @@ -109,6 +111,145 @@ class PackTransform : public UntrainableTransform
109 111  
110 112 BR_REGISTER(Transform, PackTransform)
111 113  
  114 +QVector<Mat> ProductQuantizationLUTs;
  115 +
  116 +/*!
  117 + * \ingroup distances
  118 + * \brief Distance in a product quantized space \cite jegou11
  119 + * \author Josh Klontz
  120 + */
  121 +class ProductQuantizationDistance : public Distance
  122 +{
  123 + Q_OBJECT
  124 + Q_PROPERTY(bool bayesian READ get_bayesian WRITE set_bayesian RESET reset_bayesian STORED false)
  125 + BR_PROPERTY(bool, bayesian, false)
  126 +
  127 + float compare(const Template &a, const Template &b) const
  128 + {
  129 + float distance = 0;
  130 + for (int i=0; i<a.size(); i++) {
  131 + const int elements = a[i].total();
  132 + const uchar *aData = a[i].data;
  133 + const uchar *bData = b[i].data;
  134 + const float *lut = (const float*)ProductQuantizationLUTs[i].data;
  135 + for (int j=0; j<elements; j++)
  136 + distance += lut[i*256*256 + aData[j]*256+bData[j]];
  137 + }
  138 + if (!bayesian) distance = -log(distance+1);
  139 + return distance;
  140 + }
  141 +};
  142 +
  143 +BR_REGISTER(Distance, ProductQuantizationDistance)
  144 +
  145 +/*!
  146 + * \ingroup transforms
  147 + * \brief Product quantization \cite jegou11
  148 + * \author Josh Klontz \cite jklontz
  149 + */
  150 +class ProductQuantizationTransform : public Transform
  151 +{
  152 + Q_OBJECT
  153 + Q_PROPERTY(int n READ get_n WRITE set_n RESET reset_n STORED false)
  154 + Q_PROPERTY(bool bayesian READ get_bayesian WRITE set_bayesian RESET reset_bayesian STORED false)
  155 + BR_PROPERTY(int, n, 2)
  156 + BR_PROPERTY(bool, bayesian, false)
  157 +
  158 + int index;
  159 + QList<Mat> centers;
  160 +
  161 +public:
  162 + ProductQuantizationTransform()
  163 + {
  164 + index = ProductQuantizationLUTs.size();
  165 + ProductQuantizationLUTs.append(Mat());
  166 + }
  167 +
  168 +private:
  169 + static double likelihoodRatio(const QPair<int,int> &totals, const QList<int> &targets, const QList<int> &queries)
  170 + {
  171 + int positives = 0, negatives = 0;
  172 + foreach (int t, targets)
  173 + foreach (int q, queries)
  174 + if (t == q) positives++;
  175 + else negatives++;
  176 + return log(float(positives)/float(totals.first)) / (float(negatives)/float(totals.second));
  177 + }
  178 +
  179 + void _train(const Mat &data, const QPair<int,int> &totals, Mat &lut, int i, const QList<int> &templateLabels)
  180 + {
  181 + Mat labels, center;
  182 + kmeans(data.colRange(i*n,(i+1)*n), 256, labels, TermCriteria(TermCriteria::MAX_ITER, 10, 0), 3, KMEANS_PP_CENTERS, center);
  183 + QList<int> clusterLabels = OpenCVUtils::matrixToVector<int>(labels);
  184 + QHash< int, QList<int> > clusters; // QHash<clusterLabel, QList<templateLabel> >
  185 + for (int i=0; i<clusterLabels.size(); i++)
  186 + clusters[clusterLabels[i]].append(templateLabels[i]);
  187 +
  188 + for (int j=0; j<256; j++)
  189 + for (int k=0; k<256; k++)
  190 + lut.at<float>(i,j*256+k) = bayesian ? likelihoodRatio(totals, clusters[j], clusters[k]) :
  191 + norm(center.row(j), center.row(k), NORM_L2);
  192 + centers[i] = center;
  193 + }
  194 +
  195 + void train(const TemplateList &src)
  196 + {
  197 + Mat data = OpenCVUtils::toMat(src.data());
  198 + if (data.cols % n != 0) qFatal("Expected dimensionality to be divisible by n.");
  199 + const QList<int> templateLabels = src.labels<int>();
  200 + int totalPositives = 0, totalNegatives = 0;
  201 + for (int i=0; i<templateLabels.size(); i++)
  202 + for (int j=0; j<templateLabels.size(); j++)
  203 + if (templateLabels[i] == templateLabels[j]) totalPositives++;
  204 + else totalNegatives++;
  205 + QPair<int,int> totals(totalPositives, totalNegatives);
  206 +
  207 + Mat &lut = ProductQuantizationLUTs[index];
  208 + lut = Mat(data.cols/n, 256*256, CV_32FC1);
  209 +
  210 + for (int i=0; i<lut.rows; i++)
  211 + centers.append(Mat());
  212 +
  213 + QFutureSynchronizer<void> futures;
  214 + for (int i=0; i<lut.rows; i++) {
  215 + if (Globals->parallelism) futures.addFuture(QtConcurrent::run(this, &ProductQuantizationTransform::_train, data, totals, lut, i, templateLabels));
  216 + else _train (data, totals, lut, i, templateLabels);
  217 + }
  218 + futures.waitForFinished();
  219 + }
  220 +
  221 + void project(const Template &src, Template &dst) const
  222 + {
  223 + Mat m = src.m().reshape(1, 1);
  224 + dst = Mat(1, m.cols/n, CV_8UC1);
  225 + for (int i=0; i<dst.m().cols; i++) {
  226 + int bestIndex = 0;
  227 + double bestDistance = std::numeric_limits<double>::max();
  228 + Mat m_i = m.colRange(i*n, (i+1)*n);
  229 + for (int j=0; j<256; j++) {
  230 + double distance = norm(m_i, centers[index].row(j), NORM_L2);
  231 + if (distance < bestDistance) {
  232 + bestDistance = distance;
  233 + bestIndex = j;
  234 + }
  235 + }
  236 + dst.m().at<uchar>(0,i) = bestIndex;
  237 + }
  238 + }
  239 +
  240 + void store(QDataStream &stream) const
  241 + {
  242 + stream << centers << ProductQuantizationLUTs[index];
  243 + }
  244 +
  245 + void load(QDataStream &stream)
  246 + {
  247 + stream >> centers >> ProductQuantizationLUTs[index];
  248 + }
  249 +};
  250 +
  251 +BR_REGISTER(Transform, ProductQuantizationTransform)
  252 +
112 253 } // namespace br
113 254  
114 255 #include "quantize.moc"
... ...
sdk/plugins/regions.cpp
... ... @@ -120,6 +120,7 @@ class MergeTransform : public UntrainableMetaTransform
120 120  
121 121 void project(const Template &src, Template &dst) const
122 122 {
  123 + dst.file = src.file;
123 124 std::vector<Mat> mv;
124 125 foreach (const Mat &m, src)
125 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 2 #include <QtConcurrentRun>
2 3 #include <openbr_plugin.h>
3 4  
... ... @@ -34,16 +35,16 @@ class CrossValidateTransform : public MetaTransform
34 35 return;
35 36 }
36 37  
37   - QList< QFuture<void> > futures;
  38 + QFutureSynchronizer<void> futures;
38 39 for (int i=0; i<numPartitions; i++) {
39 40 TemplateList partitionedData = data;
40 41 for (int j=partitionedData.size()-1; j>=0; j--)
41 42 if (partitions[j] == i)
42 43 partitionedData.removeAt(j);
43   - if (Globals->parallelism) futures.append(QtConcurrent::run(transforms[i], &Transform::train, partitionedData));
44   - else transforms[i]->train(partitionedData);
  44 + if (Globals->parallelism) futures.addFuture(QtConcurrent::run(transforms[i], &Transform::train, partitionedData));
  45 + else transforms[i]->train(partitionedData);
45 46 }
46   - Globals->trackFutures(futures);
  47 + futures.waitForFinished();
47 48 }
48 49  
49 50 void project(const Template &src, Template &dst) const
... ...
sdk/plugins/youtube.cpp
  1 +#include <QCoreApplication>
1 2 #include <QProcess>
2 3 #include <openbr_plugin.h>
3 4  
... ...
1   -Subproject commit 5dcad735fc16ce86937f992df8d4ba274dc81c14
  1 +Subproject commit c11ff0f0bb451fb5576bd4b8122c32278e52042a
... ...
share/openbr/openbr.bib
... ... @@ -33,7 +33,6 @@
33 33 Author = {Lacey S. Best-Rowden},
34 34 Howpublished = {https://github.com/lbestrowden},
35 35 Title = {bestrow1 at msu.edu}}
36   -
37 36  
38 37 % Software
39 38 @misc{OpenBR,
... ... @@ -87,6 +86,14 @@
87 86  
88 87  
89 88 % Papers
  89 +@ inproceedings{Arandjelovic12,
  90 + Author={Arandjelovic, R. and Zisserman, A.},
  91 + Booktitle={Computer Vision and Pattern Recognition (CVPR), 2012 IEEE Conference on},
  92 + Title={Three things everyone should know to improve object retrieval},
  93 + Month={June},
  94 + Year={2012}
  95 + Pages={2911-2918}}
  96 +
90 97 @inproceedings{belhumeur11,
91 98 Author = {Belhumeur, P.N. and Jacobs, D.W. and Kriegman, D.J. and Kumar, N.},
92 99 Booktitle = {Computer Vision and Pattern Recognition (CVPR), 2011 IEEE Conference on},
... ...