Commit cfa577b513e8d43b8b73473fdaa7f5be09d6e439

Authored by Scott Klum
2 parents f605bccf c2b1835e

Removed debug statement in core

.gitignore
@@ -20,6 +20,7 @@ scripts/results @@ -20,6 +20,7 @@ scripts/results
20 20
21 ### QtCreator ### 21 ### QtCreator ###
22 *CMakeLists.txt.user* 22 *CMakeLists.txt.user*
  23 +*.autosave
23 24
24 ### R ### 25 ### R ###
25 *.RData 26 *.RData
CHANGELOG.md
1 0.4.0 - ??/??/?? 1 0.4.0 - ??/??/??
2 ================ 2 ================
3 * Added -evalDetection and -plotDetection for evaluating and plotting object detection accuracy (#9) 3 * Added -evalDetection and -plotDetection for evaluating and plotting object detection accuracy (#9)
  4 +* Deprecated Transform::backProject
4 5
5 0.3.0 - 5/22/13 6 0.3.0 - 5/22/13
6 =============== 7 ===============
README.md
1 -www.openbiometrics.org 1 +**www.openbiometrics.org**
2 2
3 $ git clone https://github.com/biometrics/openbr.git 3 $ git clone https://github.com/biometrics/openbr.git
4 $ cd openbr 4 $ cd openbr
5 $ git submodule init 5 $ git submodule init
6 $ git submodule update 6 $ git submodule update
  7 +
  8 +To optionally check out a particular [tagged release](https://github.com/biometrics/openbr/releases):
7 9
8 -[Build Instructions](http://openbiometrics.org/doxygen/latest/installation.html) 10 + $ git checkout <tag>
  11 + $ git submodule update
  12 +
  13 +**[Build Instructions](http://openbiometrics.org/doxygen/latest/installation.html)**
app/br/br.cpp
@@ -129,17 +129,20 @@ public: @@ -129,17 +129,20 @@ public:
129 check(parc == 3, "Incorrect parameter count for 'convert'."); 129 check(parc == 3, "Incorrect parameter count for 'convert'.");
130 br_convert(parv[0], parv[1], parv[2]); 130 br_convert(parv[0], parv[1], parv[2]);
131 } else if (!strcmp(fun, "evalClassification")) { 131 } else if (!strcmp(fun, "evalClassification")) {
132 - check(parc == 2, "Incorrect parameter count for 'evalClassification'.");  
133 - br_eval_classification(parv[0], parv[1]); 132 + check(parc >= 2 && parc <= 4, "Incorrect parameter count for 'evalClassification'.");
  133 + br_eval_classification(parv[0], parv[1], parc >= 3 ? parv[2] : "", parc >= 4 ? parv[3] : "");
134 } else if (!strcmp(fun, "evalClustering")) { 134 } else if (!strcmp(fun, "evalClustering")) {
135 check(parc == 2, "Incorrect parameter count for 'evalClustering'."); 135 check(parc == 2, "Incorrect parameter count for 'evalClustering'.");
136 br_eval_clustering(parv[0], parv[1]); 136 br_eval_clustering(parv[0], parv[1]);
137 } else if (!strcmp(fun, "evalDetection")) { 137 } else if (!strcmp(fun, "evalDetection")) {
138 check((parc >= 2) && (parc <= 3), "Incorrect parameter count for 'evalDetection'."); 138 check((parc >= 2) && (parc <= 3), "Incorrect parameter count for 'evalDetection'.");
139 br_eval_detection(parv[0], parv[1], parc == 3 ? parv[2] : ""); 139 br_eval_detection(parv[0], parv[1], parc == 3 ? parv[2] : "");
  140 + } else if (!strcmp(fun, "evalLandmarking")) {
  141 + check((parc >= 2) && (parc <= 3), "Incorrect parameter count for 'evalLandmarking'.");
  142 + br_eval_detection(parv[0], parv[1], parc == 3 ? parv[2] : "");
140 } else if (!strcmp(fun, "evalRegression")) { 143 } else if (!strcmp(fun, "evalRegression")) {
141 - check(parc == 2, "Incorrect parameter count for 'evalRegression'.");  
142 - br_eval_regression(parv[0], parv[1]); 144 + check(parc >= 2 && parc <= 4, "Incorrect parameter count for 'evalRegression'.");
  145 + br_eval_regression(parv[0], parv[1], parc >= 3 ? parv[2] : "", parc >= 4 ? parv[3] : "");
143 } else if (!strcmp(fun, "plotDetection")) { 146 } else if (!strcmp(fun, "plotDetection")) {
144 check(parc >= 2, "Incorrect parameter count for 'plotDetection'."); 147 check(parc >= 2, "Incorrect parameter count for 'plotDetection'.");
145 br_plot_detection(parc-1, parv, parv[parc-1], true); 148 br_plot_detection(parc-1, parv, parv[parc-1], true);
@@ -215,10 +218,11 @@ private: @@ -215,10 +218,11 @@ private:
215 "-combineMasks <mask> ... <mask> {mask} (And|Or)\n" 218 "-combineMasks <mask> ... <mask> {mask} (And|Or)\n"
216 "-cat <gallery> ... <gallery> {gallery}\n" 219 "-cat <gallery> ... <gallery> {gallery}\n"
217 "-convert (Format|Gallery|Output) <input_file> {output_file}\n" 220 "-convert (Format|Gallery|Output) <input_file> {output_file}\n"
218 - "-evalClassification <predicted_gallery> <truth_gallery>\n" 221 + "-evalClassification <predicted_gallery> <truth_gallery> <predicted property name> <ground truth proprty name>\n"
219 "-evalClustering <clusters> <gallery>\n" 222 "-evalClustering <clusters> <gallery>\n"
220 "-evalDetection <predicted_gallery> <truth_gallery> [{csv}]\n" 223 "-evalDetection <predicted_gallery> <truth_gallery> [{csv}]\n"
221 - "-evalRegression <predicted_gallery> <truth_gallery>\n" 224 + "-evalLandmarking <predicted_gallery> <truth_gallery> [{csv}]\n"
  225 + "-evalRegression <predicted_gallery> <truth_gallery> <predicted property name> <ground truth property name>\n"
222 "-plotDetection <file> ... <file> {destination}\n" 226 "-plotDetection <file> ... <file> {destination}\n"
223 "-plotMetadata <file> ... <file> <columns>\n" 227 "-plotMetadata <file> ... <file> <columns>\n"
224 "-getHeader <matrix>\n" 228 "-getHeader <matrix>\n"
app/examples/age_estimation.cpp
@@ -29,7 +29,7 @@ @@ -29,7 +29,7 @@
29 29
30 static void printTemplate(const br::Template &t) 30 static void printTemplate(const br::Template &t)
31 { 31 {
32 - printf("%s age: %d\n", qPrintable(t.file.fileName()), int(t.file.get<float>("Subject"))); 32 + printf("%s age: %d\n", qPrintable(t.file.fileName()), int(t.file.get<float>("Age")));
33 } 33 }
34 34
35 int main(int argc, char *argv[]) 35 int main(int argc, char *argv[])
app/examples/face_recognition_evaluation.cpp
@@ -25,8 +25,7 @@ @@ -25,8 +25,7 @@
25 * -compare target.gal query.gal scores.mtx \ 25 * -compare target.gal query.gal scores.mtx \
26 * -makeMask ../data/MEDS/sigset/MEDS_frontal_target.xml ../data/MEDS/sigset/MEDS_frontal_query.xml MEDS.mask \ 26 * -makeMask ../data/MEDS/sigset/MEDS_frontal_target.xml ../data/MEDS/sigset/MEDS_frontal_query.xml MEDS.mask \
27 * -eval scores.mtx MEDS.mask Algorithm_Dataset/FaceRecognition_MEDS.csv \ 27 * -eval scores.mtx MEDS.mask Algorithm_Dataset/FaceRecognition_MEDS.csv \
28 - * -eval ../data/MEDS/simmat/COTS_MEDS.mtx MEDS.mask Algorithm_Dataset/COTS_MEDS.csv \  
29 - * -plot Algorithm_Dataset/FaceRecognition_MEDS.csv Algorithm_Dataset/COTS_MEDS.csv MEDS 28 + * -plot Algorithm_Dataset/FaceRecognition_MEDS.csv MEDS
30 * \endcode 29 * \endcode
31 */ 30 */
32 31
app/examples/gender_estimation.cpp
@@ -29,7 +29,7 @@ @@ -29,7 +29,7 @@
29 29
30 static void printTemplate(const br::Template &t) 30 static void printTemplate(const br::Template &t)
31 { 31 {
32 - printf("%s gender: %s\n", qPrintable(t.file.fileName()), qPrintable(t.file.get<QString>("Subject"))); 32 + printf("%s gender: %s\n", qPrintable(t.file.fileName()), qPrintable(t.file.get<QString>("Gender")));
33 } 33 }
34 34
35 int main(int argc, char *argv[]) 35 int main(int argc, char *argv[])
openbr/core/bee.cpp
@@ -99,10 +99,10 @@ void BEE::writeSigset(const QString &amp;sigset, const br::FileList &amp;files, bool ign @@ -99,10 +99,10 @@ void BEE::writeSigset(const QString &amp;sigset, const br::FileList &amp;files, bool ign
99 QStringList metadata; 99 QStringList metadata;
100 if (!ignoreMetadata) 100 if (!ignoreMetadata)
101 foreach (const QString &key, file.localKeys()) { 101 foreach (const QString &key, file.localKeys()) {
102 - if ((key == "Index") || (key == "Subject")) continue; 102 + if ((key == "Index") || (key == "Label")) continue;
103 metadata.append(key+"=\""+QtUtils::toString(file.value(key))+"\""); 103 metadata.append(key+"=\""+QtUtils::toString(file.value(key))+"\"");
104 } 104 }
105 - lines.append("\t<biometric-signature name=\"" + file.get<QString>("Subject",file.fileName()) +"\">"); 105 + lines.append("\t<biometric-signature name=\"" + file.get<QString>("Label",file.fileName()) +"\">");
106 lines.append("\t\t<presentation file-name=\"" + file.name + "\" " + metadata.join(" ") + "/>"); 106 lines.append("\t\t<presentation file-name=\"" + file.name + "\" " + metadata.join(" ") + "/>");
107 lines.append("\t</biometric-signature>"); 107 lines.append("\t</biometric-signature>");
108 } 108 }
@@ -266,10 +266,11 @@ void BEE::makeMask(const QString &amp;targetInput, const QString &amp;queryInput, const @@ -266,10 +266,11 @@ void BEE::makeMask(const QString &amp;targetInput, const QString &amp;queryInput, const
266 266
267 cv::Mat BEE::makeMask(const br::FileList &targets, const br::FileList &queries, int partition) 267 cv::Mat BEE::makeMask(const br::FileList &targets, const br::FileList &queries, int partition)
268 { 268 {
269 - // Would like to use indexProperty for this, but didn't make a version of that for Filelist yet  
270 - // -cao  
271 - QList<QString> targetLabels = File::get<QString>(targets, "Subject", "-1");  
272 - QList<QString> queryLabels = File::get<QString>(queries, "Subject", "-1"); 269 + // Direct use of "Label" isn't general, also would prefer to use indexProperty, rather than
  270 + // doing string comparisons (but that isn't implemented yet for FileList) -cao
  271 + QList<QString> targetLabels = File::get<QString>(targets, "Label", "-1");
  272 + QList<QString> queryLabels = File::get<QString>(queries, "Label", "-1");
  273 +
273 QList<int> targetPartitions = targets.crossValidationPartitions(); 274 QList<int> targetPartitions = targets.crossValidationPartitions();
274 QList<int> queryPartitions = queries.crossValidationPartitions(); 275 QList<int> queryPartitions = queries.crossValidationPartitions();
275 276
openbr/core/cluster.cpp
@@ -279,8 +279,8 @@ void br::EvalClustering(const QString &amp;csv, const QString &amp;input) @@ -279,8 +279,8 @@ void br::EvalClustering(const QString &amp;csv, const QString &amp;input)
279 qDebug("Evaluating %s against %s", qPrintable(csv), qPrintable(input)); 279 qDebug("Evaluating %s against %s", qPrintable(csv), qPrintable(input));
280 280
281 // We assume clustering algorithms store assigned cluster labels as integers (since the clusters are 281 // We assume clustering algorithms store assigned cluster labels as integers (since the clusters are
282 - // not named).  
283 - QList<int> labels = File::get<int>(TemplateList::fromGallery(input), "Subject"); 282 + // not named). Direct use of ClusterID is not general -cao
  283 + QList<int> labels = File::get<int>(TemplateList::fromGallery(input), "ClusterID");
284 284
285 QHash<int, int> labelToIndex; 285 QHash<int, int> labelToIndex;
286 int nClusters = 0; 286 int nClusters = 0;
openbr/core/common.cpp
@@ -15,11 +15,15 @@ @@ -15,11 +15,15 @@
15 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 15 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16 16
17 #include "common.h" 17 #include "common.h"
  18 +#include <QMutex>
18 19
19 using namespace std; 20 using namespace std;
20 21
21 /**** GLOBAL ****/ 22 /**** GLOBAL ****/
22 void Common::seedRNG() { 23 void Common::seedRNG() {
  24 + static QMutex seedControl;
  25 + QMutexLocker lock(&seedControl);
  26 +
23 static bool seeded = false; 27 static bool seeded = false;
24 if (!seeded) { 28 if (!seeded) {
25 srand(0); // We seed with 0 instead of time(NULL) to have reproducible randomness 29 srand(0); // We seed with 0 instead of time(NULL) to have reproducible randomness
@@ -29,8 +33,6 @@ void Common::seedRNG() { @@ -29,8 +33,6 @@ void Common::seedRNG() {
29 33
30 QList<int> Common::RandSample(int n, int max, int min, bool unique) 34 QList<int> Common::RandSample(int n, int max, int min, bool unique)
31 { 35 {
32 - seedRNG();  
33 -  
34 QList<int> samples; samples.reserve(n); 36 QList<int> samples; samples.reserve(n);
35 int range = max-min; 37 int range = max-min;
36 if (range <= 0) qFatal("Non-positive range."); 38 if (range <= 0) qFatal("Non-positive range.");
@@ -50,8 +52,6 @@ QList&lt;int&gt; Common::RandSample(int n, int max, int min, bool unique) @@ -50,8 +52,6 @@ QList&lt;int&gt; Common::RandSample(int n, int max, int min, bool unique)
50 52
51 QList<int> Common::RandSample(int n, const QSet<int> &values, bool unique) 53 QList<int> Common::RandSample(int n, const QSet<int> &values, bool unique)
52 { 54 {
53 - seedRNG();  
54 -  
55 QList<int> valueList = values.toList(); 55 QList<int> valueList = values.toList();
56 if (unique && (values.size() <= n)) return valueList; 56 if (unique && (values.size() <= n)) return valueList;
57 57
openbr/core/core.cpp
@@ -147,13 +147,7 @@ struct AlgorithmCore @@ -147,13 +147,7 @@ struct AlgorithmCore
147 data.removeAt(i); 147 data.removeAt(i);
148 const int numFiles = data.size(); 148 const int numFiles = data.size();
149 149
150 - if (Globals->backProject) {  
151 - TemplateList backProjectedData;  
152 - transform->backProject(data, backProjectedData);  
153 - data = backProjectedData;  
154 - } else {  
155 - data >> *transform;  
156 - } 150 + data >> *transform;
157 151
158 g->writeBlock(data); 152 g->writeBlock(data);
159 const FileList newFiles = data.files(); 153 const FileList newFiles = data.files();
@@ -394,7 +388,6 @@ void br::Convert(const File &amp;fileType, const File &amp;inputFile, const File &amp;output @@ -394,7 +388,6 @@ void br::Convert(const File &amp;fileType, const File &amp;inputFile, const File &amp;output
394 QSharedPointer<Output> o(Factory<Output>::make(outputFile)); 388 QSharedPointer<Output> o(Factory<Output>::make(outputFile));
395 o->initialize(targetFiles, queryFiles); 389 o->initialize(targetFiles, queryFiles);
396 390
397 - qDebug() << m.rows << m.cols << targetFiles.size() << queryFiles.size();  
398 for (int i=0; i<queryFiles.size(); i++) 391 for (int i=0; i<queryFiles.size(); i++)
399 for (int j=0; j<targetFiles.size(); j++) 392 for (int j=0; j<targetFiles.size(); j++)
400 o->setRelative(m.at<float>(i,j), i, j); 393 o->setRelative(m.at<float>(i,j), i, j);
openbr/core/eval.cpp
@@ -255,9 +255,20 @@ struct Counter @@ -255,9 +255,20 @@ struct Counter
255 } 255 }
256 }; 256 };
257 257
258 -void EvalClassification(const QString &predictedInput, const QString &truthInput) 258 +void EvalClassification(const QString &predictedInput, const QString &truthInput, QString predictedProperty, QString truthProperty)
259 { 259 {
260 qDebug("Evaluating classification of %s against %s", qPrintable(predictedInput), qPrintable(truthInput)); 260 qDebug("Evaluating classification of %s against %s", qPrintable(predictedInput), qPrintable(truthInput));
  261 +
  262 + if (predictedProperty.isEmpty())
  263 + predictedProperty = "Label";
  264 + // If predictedProperty is specified, but truthProperty isn't, copy over the value from
  265 + // predicted property
  266 + else if (truthProperty.isEmpty())
  267 + truthProperty = predictedProperty;
  268 +
  269 + if (truthProperty.isEmpty())
  270 + truthProperty = "Label";
  271 +
261 TemplateList predicted(TemplateList::fromGallery(predictedInput)); 272 TemplateList predicted(TemplateList::fromGallery(predictedInput));
262 TemplateList truth(TemplateList::fromGallery(truthInput)); 273 TemplateList truth(TemplateList::fromGallery(truthInput));
263 if (predicted.size() != truth.size()) qFatal("Input size mismatch."); 274 if (predicted.size() != truth.size()) qFatal("Input size mismatch.");
@@ -267,9 +278,8 @@ void EvalClassification(const QString &amp;predictedInput, const QString &amp;truthInput @@ -267,9 +278,8 @@ void EvalClassification(const QString &amp;predictedInput, const QString &amp;truthInput
267 if (predicted[i].file.name != truth[i].file.name) 278 if (predicted[i].file.name != truth[i].file.name)
268 qFatal("Input order mismatch."); 279 qFatal("Input order mismatch.");
269 280
270 - // Typically these lists will be of length one, but this generalization allows measuring multi-class labeling accuracy.  
271 - QString predictedSubject = predicted[i].file.get<QString>("Subject");  
272 - QString trueSubject = truth[i].file.get<QString>("Subject"); 281 + QString predictedSubject = predicted[i].file.get<QString>(predictedProperty);
  282 + QString trueSubject = truth[i].file.get<QString>(truthProperty);
273 283
274 QStringList predictedSubjects(predictedSubject); 284 QStringList predictedSubjects(predictedSubject);
275 QStringList trueSubjects(trueSubject); 285 QStringList trueSubjects(trueSubject);
@@ -373,6 +383,8 @@ static QStringList computeDetectionResults(const QList&lt;ResolvedDetection&gt; &amp;detec @@ -373,6 +383,8 @@ static QStringList computeDetectionResults(const QList&lt;ResolvedDetection&gt; &amp;detec
373 } 383 }
374 384
375 const int keep = qMin(points.size(), Max_Points); 385 const int keep = qMin(points.size(), Max_Points);
  386 + if (keep < 2) qFatal("Insufficient points.");
  387 +
376 QStringList lines; lines.reserve(keep); 388 QStringList lines; lines.reserve(keep);
377 for (int i=0; i<keep; i++) { 389 for (int i=0; i<keep; i++) {
378 const DetectionOperatingPoint &point = points[double(i) / double(keep-1) * double(points.size()-1)]; 390 const DetectionOperatingPoint &point = points[double(i) / double(keep-1) * double(points.size()-1)];
@@ -382,6 +394,15 @@ static QStringList computeDetectionResults(const QList&lt;ResolvedDetection&gt; &amp;detec @@ -382,6 +394,15 @@ static QStringList computeDetectionResults(const QList&lt;ResolvedDetection&gt; &amp;detec
382 return lines; 394 return lines;
383 } 395 }
384 396
  397 +QString getDetectKey(const TemplateList &templates)
  398 +{
  399 + const File &f = templates.first().file;
  400 + foreach (const QString &key, f.localKeys())
  401 + if (!f.get<QRectF>(key, QRectF()).isNull())
  402 + return key;
  403 + return "";
  404 +}
  405 +
385 float EvalDetection(const QString &predictedInput, const QString &truthInput, const QString &csv) 406 float EvalDetection(const QString &predictedInput, const QString &truthInput, const QString &csv)
386 { 407 {
387 qDebug("Evaluating detection of %s against %s", qPrintable(predictedInput), qPrintable(truthInput)); 408 qDebug("Evaluating detection of %s against %s", qPrintable(predictedInput), qPrintable(truthInput));
@@ -389,20 +410,22 @@ float EvalDetection(const QString &amp;predictedInput, const QString &amp;truthInput, co @@ -389,20 +410,22 @@ float EvalDetection(const QString &amp;predictedInput, const QString &amp;truthInput, co
389 const TemplateList truth(TemplateList::fromGallery(truthInput)); 410 const TemplateList truth(TemplateList::fromGallery(truthInput));
390 411
391 // Figure out which metadata field contains a bounding box 412 // Figure out which metadata field contains a bounding box
392 - QString detectKey;  
393 - foreach (const QString &key, truth.first().file.localKeys())  
394 - if (!truth.first().file.get<QRectF>(key, QRectF()).isNull()) {  
395 - detectKey = key;  
396 - break;  
397 - }  
398 - if (detectKey.isNull()) qFatal("No suitable metadata key found.");  
399 - else qDebug("Using metadata key: %s", qPrintable(detectKey)); 413 + QString truthDetectKey = getDetectKey(truth);
  414 + if (truthDetectKey.isEmpty()) qFatal("No suitable ground truth metadata key found.");
  415 + QString predictedDetectKey = truthDetectKey;
  416 + if (predicted.first().file.get<QRectF>(predictedDetectKey, QRectF()).isNull())
  417 + predictedDetectKey = getDetectKey(predicted);
  418 + if (predictedDetectKey.isEmpty()) qFatal("No suitable predicted metadata key found.");
  419 +
  420 + qDebug("Using metadata key: %s%s",
  421 + qPrintable(predictedDetectKey),
  422 + qPrintable(predictedDetectKey == truthDetectKey ? QString() : "/"+truthDetectKey));
400 423
401 QMap<QString, Detections> allDetections; // Organized by file, QMap used to preserve order 424 QMap<QString, Detections> allDetections; // Organized by file, QMap used to preserve order
402 foreach (const Template &t, predicted) 425 foreach (const Template &t, predicted)
403 - allDetections[t.file.baseName()].predicted.append(Detection(t.file.get<QRectF>(detectKey), t.file.get<float>("Confidence", -1))); 426 + allDetections[t.file.baseName()].predicted.append(Detection(t.file.get<QRectF>(predictedDetectKey), t.file.get<float>("Confidence", -1)));
404 foreach (const Template &t, truth) 427 foreach (const Template &t, truth)
405 - allDetections[t.file.baseName()].truth.append(Detection(t.file.get<QRectF>(detectKey))); 428 + allDetections[t.file.baseName()].truth.append(Detection(t.file.get<QRectF>(truthDetectKey)));
406 429
407 QList<ResolvedDetection> resolvedDetections, falseNegativeDetections; 430 QList<ResolvedDetection> resolvedDetections, falseNegativeDetections;
408 foreach (Detections detections, allDetections.values()) { 431 foreach (Detections detections, allDetections.values()) {
@@ -453,21 +476,44 @@ float EvalDetection(const QString &amp;predictedInput, const QString &amp;truthInput, co @@ -453,21 +476,44 @@ float EvalDetection(const QString &amp;predictedInput, const QString &amp;truthInput, co
453 return averageOverlap; 476 return averageOverlap;
454 } 477 }
455 478
456 -void EvalRegression(const QString &predictedInput, const QString &truthInput) 479 +void EvalLandmarking(const QString &predictedInput, const QString &truthInput, const QString &csv)
  480 +{
  481 + (void) predictedInput;
  482 + (void) truthInput;
  483 + (void) csv;
  484 +}
  485 +
  486 +void EvalRegression(const QString &predictedInput, const QString &truthInput, QString predictedProperty, QString truthProperty)
457 { 487 {
458 qDebug("Evaluating regression of %s against %s", qPrintable(predictedInput), qPrintable(truthInput)); 488 qDebug("Evaluating regression of %s against %s", qPrintable(predictedInput), qPrintable(truthInput));
  489 +
  490 + if (predictedProperty.isEmpty())
  491 + predictedProperty = "Regressor";
  492 + // If predictedProperty is specified, but truthProperty isn't, copy the value over
  493 + // rather than using the default for truthProperty
  494 + else if (truthProperty.isEmpty())
  495 + truthProperty = predictedProperty;
  496 +
  497 + if (truthProperty.isEmpty())
  498 + predictedProperty = "Regressand";
  499 +
459 const TemplateList predicted(TemplateList::fromGallery(predictedInput)); 500 const TemplateList predicted(TemplateList::fromGallery(predictedInput));
460 const TemplateList truth(TemplateList::fromGallery(truthInput)); 501 const TemplateList truth(TemplateList::fromGallery(truthInput));
461 if (predicted.size() != truth.size()) qFatal("Input size mismatch."); 502 if (predicted.size() != truth.size()) qFatal("Input size mismatch.");
462 503
463 float rmsError = 0; 504 float rmsError = 0;
  505 + float maeError = 0;
464 QStringList truthValues, predictedValues; 506 QStringList truthValues, predictedValues;
465 for (int i=0; i<predicted.size(); i++) { 507 for (int i=0; i<predicted.size(); i++) {
466 if (predicted[i].file.name != truth[i].file.name) 508 if (predicted[i].file.name != truth[i].file.name)
467 qFatal("Input order mismatch."); 509 qFatal("Input order mismatch.");
468 - rmsError += pow(predicted[i].file.get<float>("Subject")-truth[i].file.get<float>("Subject"), 2.f);  
469 - truthValues.append(QString::number(truth[i].file.get<float>("Subject")));  
470 - predictedValues.append(QString::number(predicted[i].file.get<float>("Subject"))); 510 +
  511 + float difference = predicted[i].file.get<float>(predictedProperty) - truth[i].file.get<float>(truthProperty);
  512 +
  513 + rmsError += pow(difference, 2.f);
  514 + maeError += fabsf(difference);
  515 + truthValues.append(QString::number(truth[i].file.get<float>(truthProperty)));
  516 + predictedValues.append(QString::number(predicted[i].file.get<float>(predictedProperty)));
471 } 517 }
472 518
473 QStringList rSource; 519 QStringList rSource;
@@ -487,6 +533,7 @@ void EvalRegression(const QString &amp;predictedInput, const QString &amp;truthInput) @@ -487,6 +533,7 @@ void EvalRegression(const QString &amp;predictedInput, const QString &amp;truthInput)
487 if (success) QtUtils::showFile("EvalRegression.pdf"); 533 if (success) QtUtils::showFile("EvalRegression.pdf");
488 534
489 qDebug("RMS Error = %f", sqrt(rmsError/predicted.size())); 535 qDebug("RMS Error = %f", sqrt(rmsError/predicted.size()));
  536 + qDebug("MAE = %f", maeError/predicted.size());
490 } 537 }
491 538
492 } // namespace br 539 } // namespace br
openbr/core/eval.h
@@ -26,9 +26,10 @@ namespace br @@ -26,9 +26,10 @@ namespace br
26 float Evaluate(const QString &simmat, const QString &mask = "", const QString &csv = ""); // Returns TAR @ FAR = 0.001 26 float Evaluate(const QString &simmat, const QString &mask = "", const QString &csv = ""); // Returns TAR @ FAR = 0.001
27 float Evaluate(const cv::Mat &scores, const FileList &target, const FileList &query, const QString &csv = "", int parition = 0); 27 float Evaluate(const cv::Mat &scores, const FileList &target, const FileList &query, const QString &csv = "", int parition = 0);
28 float Evaluate(const cv::Mat &scores, const cv::Mat &masks, const QString &csv = ""); 28 float Evaluate(const cv::Mat &scores, const cv::Mat &masks, const QString &csv = "");
29 - void EvalClassification(const QString &predictedInput, const QString &truthInput); 29 + void EvalClassification(const QString &predictedInput, const QString &truthInput, QString predictedProperty="", QString truthProperty="");
30 float EvalDetection(const QString &predictedInput, const QString &truthInput, const QString &csv = ""); // Return average overlap 30 float EvalDetection(const QString &predictedInput, const QString &truthInput, const QString &csv = ""); // Return average overlap
31 - void EvalRegression(const QString &predictedInput, const QString &truthInput); 31 + void EvalLandmarking(const QString &predictedInput, const QString &truthInput, const QString &csv = "");
  32 + void EvalRegression(const QString &predictedInput, const QString &truthInput, QString predictedProperty="", QString truthProperty="");
32 } 33 }
33 34
34 #endif // __EVAL_H 35 #endif // __EVAL_H
openbr/core/qtutils.cpp
@@ -334,8 +334,8 @@ QRectF QtUtils::toRect(const QString &amp;string, bool *ok) @@ -334,8 +334,8 @@ QRectF QtUtils::toRect(const QString &amp;string, bool *ok)
334 bool okX, okY, okWidth, okHeight; 334 bool okX, okY, okWidth, okHeight;
335 x = words[0].toFloat(&okX); 335 x = words[0].toFloat(&okX);
336 y = words[1].toFloat(&okY); 336 y = words[1].toFloat(&okY);
337 - width = words[0].toFloat(&okWidth);  
338 - height = words[1].toFloat(&okHeight); 337 + width = words[2].toFloat(&okWidth);
  338 + height = words[3].toFloat(&okHeight);
339 if (okX && okY && okWidth && okHeight) { 339 if (okX && okY && okWidth && okHeight) {
340 if (ok) *ok = true; 340 if (ok) *ok = true;
341 return QRectF(x, y, width, height); 341 return QRectF(x, y, width, height);
openbr/core/resource.h
@@ -24,6 +24,7 @@ @@ -24,6 +24,7 @@
24 #include <QSharedPointer> 24 #include <QSharedPointer>
25 #include <QString> 25 #include <QString>
26 #include <QThread> 26 #include <QThread>
  27 +#include <openbr/openbr_plugin.h>
27 28
28 template <typename T> 29 template <typename T>
29 class ResourceMaker 30 class ResourceMaker
@@ -52,7 +53,7 @@ public: @@ -52,7 +53,7 @@ public:
52 : resourceMaker(rm) 53 : resourceMaker(rm)
53 , availableResources(new QList<T*>()) 54 , availableResources(new QList<T*>())
54 , lock(new QMutex()) 55 , lock(new QMutex())
55 - , totalResources(new QSemaphore(QThread::idealThreadCount())) 56 + , totalResources(new QSemaphore(br::Globals->parallelism))
56 {} 57 {}
57 58
58 ~Resource() 59 ~Resource()
openbr/frvt2012.cpp
@@ -132,7 +132,7 @@ int32_t SdkEstimator::estimate_age(const ONEFACE &amp;input_face, int32_t &amp;age) @@ -132,7 +132,7 @@ int32_t SdkEstimator::estimate_age(const ONEFACE &amp;input_face, int32_t &amp;age)
132 TemplateList templates; 132 TemplateList templates;
133 templates.append(templateFromONEFACE(input_face)); 133 templates.append(templateFromONEFACE(input_face));
134 templates >> *frvt2012_age_transform.data(); 134 templates >> *frvt2012_age_transform.data();
135 - age = templates.first().file.get<float>("Subject"); 135 + age = templates.first().file.get<float>("Age");
136 return templates.first().file.failed() ? 4 : 0; 136 return templates.first().file.failed() ? 4 : 0;
137 } 137 }
138 138
@@ -141,6 +141,6 @@ int32_t SdkEstimator::estimate_gender(const ONEFACE &amp;input_face, int8_t &amp;gender, @@ -141,6 +141,6 @@ int32_t SdkEstimator::estimate_gender(const ONEFACE &amp;input_face, int8_t &amp;gender,
141 TemplateList templates; 141 TemplateList templates;
142 templates.append(templateFromONEFACE(input_face)); 142 templates.append(templateFromONEFACE(input_face));
143 templates >> *frvt2012_gender_transform.data(); 143 templates >> *frvt2012_gender_transform.data();
144 - mf = gender = templates.first().file.get<QString>("Subject") == "Male" ? 0 : 1; 144 + mf = gender = templates.first().file.get<QString>("Gender") == "Male" ? 0 : 1;
145 return templates.first().file.failed() ? 4 : 0; 145 return templates.first().file.failed() ? 4 : 0;
146 } 146 }
openbr/gui/classifier.cpp
@@ -39,19 +39,23 @@ void Classifier::_classify(File file) @@ -39,19 +39,23 @@ void Classifier::_classify(File file)
39 { 39 {
40 QString key, value; 40 QString key, value;
41 foreach (const File &f, Enroll(file.flat(), File("[algorithm=" + algorithm + "]"))) { 41 foreach (const File &f, Enroll(file.flat(), File("[algorithm=" + algorithm + "]"))) {
42 - if (!f.contains("Label"))  
43 - continue;  
44 42
45 if (algorithm == "GenderClassification") { 43 if (algorithm == "GenderClassification") {
46 key = "Gender"; 44 key = "Gender";
47 - value = (f.get<QString>("Subject"));  
48 } else if (algorithm == "AgeRegression") { 45 } else if (algorithm == "AgeRegression") {
49 key = "Age"; 46 key = "Age";
50 - value = QString::number(int(f.get<float>("Subject")+0.5)) + " Years";  
51 } else { 47 } else {
52 key = algorithm; 48 key = algorithm;
53 - value = f.get<QString>("Subject");  
54 } 49 }
  50 +
  51 + if (!f.contains(key))
  52 + continue;
  53 +
  54 + if (algorithm == "AgeRegression")
  55 + value = QString::number(int(f.get<float>(key)+0.5)) + " Years";
  56 + else
  57 + value = f.get<QString>(key);
  58 +
55 break; 59 break;
56 } 60 }
57 61
openbr/openbr.cpp
@@ -72,9 +72,9 @@ float br_eval(const char *simmat, const char *mask, const char *csv) @@ -72,9 +72,9 @@ float br_eval(const char *simmat, const char *mask, const char *csv)
72 return Evaluate(simmat, mask, csv); 72 return Evaluate(simmat, mask, csv);
73 } 73 }
74 74
75 -void br_eval_classification(const char *predicted_gallery, const char *truth_gallery) 75 +void br_eval_classification(const char *predicted_gallery, const char *truth_gallery, const char *predicted_property, const char * truth_property)
76 { 76 {
77 - EvalClassification(predicted_gallery, truth_gallery); 77 + EvalClassification(predicted_gallery, truth_gallery, predicted_property, truth_property);
78 } 78 }
79 79
80 void br_eval_clustering(const char *csv, const char *gallery) 80 void br_eval_clustering(const char *csv, const char *gallery)
@@ -87,9 +87,14 @@ float br_eval_detection(const char *predicted_gallery, const char *truth_gallery @@ -87,9 +87,14 @@ float br_eval_detection(const char *predicted_gallery, const char *truth_gallery
87 return EvalDetection(predicted_gallery, truth_gallery, csv); 87 return EvalDetection(predicted_gallery, truth_gallery, csv);
88 } 88 }
89 89
90 -void br_eval_regression(const char *predicted_gallery, const char *truth_gallery) 90 +void br_eval_landmarking(const char *predicted_gallery, const char *truth_gallery, const char *csv)
91 { 91 {
92 - EvalRegression(predicted_gallery, truth_gallery); 92 + return EvalLandmarking(predicted_gallery, truth_gallery, csv);
  93 +}
  94 +
  95 +void br_eval_regression(const char *predicted_gallery, const char *truth_gallery, const char * predicted_property, const char * truth_property)
  96 +{
  97 + EvalRegression(predicted_gallery, truth_gallery, predicted_property, truth_property);
93 } 98 }
94 99
95 void br_finalize() 100 void br_finalize()
openbr/openbr.h
@@ -149,7 +149,7 @@ BR_EXPORT float br_eval(const char *simmat, const char *mask, const char *csv = @@ -149,7 +149,7 @@ BR_EXPORT float br_eval(const char *simmat, const char *mask, const char *csv =
149 * \param predicted_gallery The predicted br::Gallery. 149 * \param predicted_gallery The predicted br::Gallery.
150 * \param truth_gallery The ground truth br::Gallery. 150 * \param truth_gallery The ground truth br::Gallery.
151 */ 151 */
152 -BR_EXPORT void br_eval_classification(const char *predicted_gallery, const char *truth_gallery); 152 +BR_EXPORT void br_eval_classification(const char *predicted_gallery, const char *truth_gallery, const char * predicted_property="", const char * truth_property="");
153 153
154 /*! 154 /*!
155 * \brief Evaluates and prints clustering accuracy to the terminal. 155 * \brief Evaluates and prints clustering accuracy to the terminal.
@@ -169,11 +169,19 @@ BR_EXPORT void br_eval_clustering(const char *csv, const char *gallery); @@ -169,11 +169,19 @@ BR_EXPORT void br_eval_clustering(const char *csv, const char *gallery);
169 BR_EXPORT float br_eval_detection(const char *predicted_gallery, const char *truth_gallery, const char *csv = ""); 169 BR_EXPORT float br_eval_detection(const char *predicted_gallery, const char *truth_gallery, const char *csv = "");
170 170
171 /*! 171 /*!
  172 + * \brief Evaluates and prints landmarking accuracy to terminal.
  173 + * \param predicted_gallery The predicted br::Gallery.
  174 + * \param truth_gallery The ground truth br::Gallery.
  175 + * \param csv Optional \c .csv file to contain performance metrics.
  176 + */
  177 +BR_EXPORT void br_eval_landmarking(const char *predicted_gallery, const char *truth_gallery, const char *csv = "");
  178 +
  179 +/*!
172 * \brief Evaluates regression accuracy to disk. 180 * \brief Evaluates regression accuracy to disk.
173 * \param predicted_gallery The predicted br::Gallery. 181 * \param predicted_gallery The predicted br::Gallery.
174 * \param truth_gallery The ground truth br::Gallery. 182 * \param truth_gallery The ground truth br::Gallery.
175 */ 183 */
176 -BR_EXPORT void br_eval_regression(const char *predicted_gallery, const char *truth_gallery); 184 +BR_EXPORT void br_eval_regression(const char *predicted_gallery, const char *truth_gallery, const char * predicted_property="", const char * truth_property="");
177 185
178 /*! 186 /*!
179 * \brief Wraps br::Context::finalize() 187 * \brief Wraps br::Context::finalize()
openbr/openbr_export.cpp
@@ -69,7 +69,6 @@ @@ -69,7 +69,6 @@
69 $ cd bin 69 $ cd bin
70 $ export LD_LIBRARY_PATH=../lib:${LD_LIBRARY_PATH} 70 $ export LD_LIBRARY_PATH=../lib:${LD_LIBRARY_PATH}
71 $ sudo ldconfig 71 $ sudo ldconfig
72 -$ sudo cp ../share/openbr/70-yubikey.rules /etc/udev/rules.d # Only needed if you were given a license dongle.  
73 \endverbatim 72 \endverbatim
74 * \par OS X 73 * \par OS X
75 \verbatim 74 \verbatim
@@ -80,10 +79,6 @@ $ export DYLD_FRAMEWORK_PATH=../lib:${DYLD_FRAMEWORK_PATH} @@ -80,10 +79,6 @@ $ export DYLD_FRAMEWORK_PATH=../lib:${DYLD_FRAMEWORK_PATH}
80 * \par Windows 79 * \par Windows
81 * No configuration is necessary! 80 * No configuration is necessary!
82 * 81 *
83 - * \section installation_license_dongle License Dongle  
84 - * In the unlikely event that you were given a USB License Dongle, then dongle must be in the computer in order to use the SDK.  
85 - * No configuration of the dongle is needed.  
86 - *  
87 * \section installation_done Start Working 82 * \section installation_done Start Working
88 * To test for successful installation: 83 * To test for successful installation:
89 \verbatim 84 \verbatim
openbr/openbr_plugin.cpp
@@ -437,6 +437,18 @@ TemplateList TemplateList::fromGallery(const br::File &amp;gallery) @@ -437,6 +437,18 @@ TemplateList TemplateList::fromGallery(const br::File &amp;gallery)
437 // Select the right 8 hex characters so that it can be represented as a 64 bit integer without overflow 437 // Select the right 8 hex characters so that it can be represented as a 64 bit integer without overflow
438 newTemplates[i].file.set("Partition", md5.toHex().right(8).toULongLong(0, 16) % crossValidate); 438 newTemplates[i].file.set("Partition", md5.toHex().right(8).toULongLong(0, 16) % crossValidate);
439 } 439 }
  440 +<<<<<<< HEAD
  441 +=======
  442 + } else if (newTemplates[i].file.getBool("allPartitions")) {
  443 + // The allPartitions flag is used to add an extended set
  444 + // of target images to every partition
  445 + newTemplates[i].file.set("Partition", -1);
  446 + } else {
  447 + // Direct use of "Label" is not general -cao
  448 + const QByteArray md5 = QCryptographicHash::hash(newTemplates[i].file.get<QString>("Label").toLatin1(), QCryptographicHash::Md5);
  449 + // Select the right 8 hex characters so that it can be represented as a 64 bit integer without overflow
  450 + newTemplates[i].file.set("Partition", md5.toHex().right(8).toULongLong(0, 16) % crossValidate);
  451 +>>>>>>> c2b1835e05d3b229db72d8829fb9ebf7e3cf31d8
440 } 452 }
441 } 453 }
442 } 454 }
@@ -834,7 +846,7 @@ float br::Context::progress() const @@ -834,7 +846,7 @@ float br::Context::progress() const
834 846
835 void br::Context::setProperty(const QString &key, const QString &value) 847 void br::Context::setProperty(const QString &key, const QString &value)
836 { 848 {
837 - Object::setProperty(key, value); 849 + Object::setProperty(key, value.isEmpty() ? QVariant() : value);
838 qDebug("Set %s%s", qPrintable(key), value.isEmpty() ? "" : qPrintable(" to " + value)); 850 qDebug("Set %s%s", qPrintable(key), value.isEmpty() ? "" : qPrintable(" to " + value));
839 851
840 if (key == "parallelism") { 852 if (key == "parallelism") {
@@ -912,6 +924,8 @@ void br::Context::initialize(int &amp;argc, char *argv[], QString sdkPath, bool use_ @@ -912,6 +924,8 @@ void br::Context::initialize(int &amp;argc, char *argv[], QString sdkPath, bool use_
912 924
913 qInstallMessageHandler(messageHandler); 925 qInstallMessageHandler(messageHandler);
914 926
  927 + Common::seedRNG();
  928 +
915 // Search for SDK 929 // Search for SDK
916 if (sdkPath.isEmpty()) { 930 if (sdkPath.isEmpty()) {
917 QStringList checkPaths; checkPaths << QDir::currentPath() << QCoreApplication::applicationDirPath(); 931 QStringList checkPaths; checkPaths << QDir::currentPath() << QCoreApplication::applicationDirPath();
@@ -1104,9 +1118,6 @@ Transform::Transform(bool _independent, bool _trainable) @@ -1104,9 +1118,6 @@ Transform::Transform(bool _independent, bool _trainable)
1104 { 1118 {
1105 independent = _independent; 1119 independent = _independent;
1106 trainable = _trainable; 1120 trainable = _trainable;
1107 - classes = std::numeric_limits<int>::max();  
1108 - instances = std::numeric_limits<int>::max();  
1109 - fraction = 1;  
1110 } 1121 }
1111 1122
1112 Transform *Transform::make(QString str, QObject *parent) 1123 Transform *Transform::make(QString str, QObject *parent)
@@ -1162,9 +1173,6 @@ Transform *Transform::make(QString str, QObject *parent) @@ -1162,9 +1173,6 @@ Transform *Transform::make(QString str, QObject *parent)
1162 Transform *Transform::clone() const 1173 Transform *Transform::clone() const
1163 { 1174 {
1164 Transform *clone = Factory<Transform>::make(file.flat()); 1175 Transform *clone = Factory<Transform>::make(file.flat());
1165 - clone->classes = classes;  
1166 - clone->instances = instances;  
1167 - clone->fraction = fraction;  
1168 return clone; 1176 return clone;
1169 } 1177 }
1170 1178
@@ -1193,28 +1201,6 @@ void Transform::project(const TemplateList &amp;src, TemplateList &amp;dst) const @@ -1193,28 +1201,6 @@ void Transform::project(const TemplateList &amp;src, TemplateList &amp;dst) const
1193 futures.waitForFinished(); 1201 futures.waitForFinished();
1194 } 1202 }
1195 1203
1196 -static void _backProject(const Transform *transform, const Template *dst, Template *src)  
1197 -{  
1198 - try {  
1199 - transform->backProject(*dst, *src);  
1200 - } catch (...) {  
1201 - qWarning("Exception triggered when processing %s with transform %s", qPrintable(src->file.flat()), qPrintable(transform->objectName()));  
1202 - *src = Template(dst->file);  
1203 - src->file.set("FTE", true);  
1204 - }  
1205 -}  
1206 -  
1207 -void Transform::backProject(const TemplateList &dst, TemplateList &src) const  
1208 -{  
1209 - src.reserve(dst.size());  
1210 - for (int i=0; i<dst.size(); i++) src.append(Template());  
1211 -  
1212 - QFutureSynchronizer<void> futures;  
1213 - for (int i=0; i<dst.size(); i++)  
1214 - futures.addFuture(QtConcurrent::run(_backProject, this, &dst[i], &src[i]));  
1215 - futures.waitForFinished();  
1216 -}  
1217 -  
1218 QList<Transform *> Transform::getChildren() const 1204 QList<Transform *> Transform::getChildren() const
1219 { 1205 {
1220 QList<Transform *> output; 1206 QList<Transform *> output;
openbr/openbr_plugin.h
@@ -130,13 +130,6 @@ void reset_##NAME() { NAME = DEFAULT; } @@ -130,13 +130,6 @@ void reset_##NAME() { NAME = DEFAULT; }
130 * -# If the value is convertable to a floating point number then it is represented with \c float. 130 * -# If the value is convertable to a floating point number then it is represented with \c float.
131 * -# Otherwise, it is represented with \c QString. 131 * -# Otherwise, it is represented with \c QString.
132 * 132 *
133 - * The metadata keys \c Subject and \c Label have special significance in the system.  
134 - * \c Subject is a string specifying a unique identifier used to determine ground truth match/non-match.  
135 - * \c Label is a floating point value used for supervised learning.  
136 - * When the system needs labels for training, but only subjects are provided in the file metadata, the rule for generating labels is as follows.  
137 - * If the subject value can be converted to a float then do so and consider that the label.  
138 - * Otherwise, generate a unique integer ID for the string starting from zero and incrementing by one everytime another ID is needed.  
139 - *  
140 * Metadata keys fall into one of two categories: 133 * Metadata keys fall into one of two categories:
141 * - \c camelCaseKeys are inputs that specify how to process the file. 134 * - \c camelCaseKeys are inputs that specify how to process the file.
142 * - \c Capitalized_Underscored_Keys are outputs computed from processing the file. 135 * - \c Capitalized_Underscored_Keys are outputs computed from processing the file.
@@ -147,8 +140,6 @@ void reset_##NAME() { NAME = DEFAULT; } @@ -147,8 +140,6 @@ void reset_##NAME() { NAME = DEFAULT; }
147 * --- | ---- | ----------- 140 * --- | ---- | -----------
148 * separator | QString | Seperate #name into multiple files 141 * separator | QString | Seperate #name into multiple files
149 * Index | int | Index of a template in a template list 142 * Index | int | Index of a template in a template list
150 - * Subject | QString | Class name  
151 - * Label | float | Class value  
152 * Confidence | float | Classification/Regression quality 143 * Confidence | float | Classification/Regression quality
153 * FTE | bool | Failure to enroll 144 * FTE | bool | Failure to enroll
154 * FTO | bool | Failure to open 145 * FTO | bool | Failure to open
@@ -157,13 +148,15 @@ void reset_##NAME() { NAME = DEFAULT; } @@ -157,13 +148,15 @@ void reset_##NAME() { NAME = DEFAULT; }
157 * *_Width | float | Size 148 * *_Width | float | Size
158 * *_Height | float | Size 149 * *_Height | float | Size
159 * *_Radius | float | Size 150 * *_Radius | float | Size
  151 + * Label | QString | Class label
160 * Theta | float | Pose 152 * Theta | float | Pose
161 * Roll | float | Pose 153 * Roll | float | Pose
162 * Pitch | float | Pose 154 * Pitch | float | Pose
163 * Yaw | float | Pose 155 * Yaw | float | Pose
164 * Points | QList<QPointF> | List of unnamed points 156 * Points | QList<QPointF> | List of unnamed points
165 * Rects | QList<Rect> | List of unnamed rects 157 * Rects | QList<Rect> | List of unnamed rects
166 - * Age | QString | Age used for demographic filtering 158 + * Age | float | Age used for demographic filtering
  159 + * Gender | QString | Subject gender
167 * _* | * | Reserved for internal use 160 * _* | * | Reserved for internal use
168 */ 161 */
169 struct BR_EXPORT File 162 struct BR_EXPORT File
@@ -172,7 +165,7 @@ struct BR_EXPORT File @@ -172,7 +165,7 @@ struct BR_EXPORT File
172 165
173 File() {} 166 File() {}
174 File(const QString &file) { init(file); } /*!< \brief Construct a file from a string. */ 167 File(const QString &file) { init(file); } /*!< \brief Construct a file from a string. */
175 - File(const QString &file, const QVariant &subject) { init(file); set("Subject", subject); } /*!< \brief Construct a file from a string and assign a label. */ 168 + File(const QString &file, const QVariant &label) { init(file); set("Label", label); } /*!< \brief Construct a file from a string and assign a label. */
176 File(const char *file) { init(file); } /*!< \brief Construct a file from a c-style string. */ 169 File(const char *file) { init(file); } /*!< \brief Construct a file from a c-style string. */
177 inline operator QString() const { return name; } /*!< \brief Returns #name. */ 170 inline operator QString() const { return name; } /*!< \brief Returns #name. */
178 QString flat() const; /*!< \brief A stringified version of the file with metadata. */ 171 QString flat() const; /*!< \brief A stringified version of the file with metadata. */
@@ -616,13 +609,6 @@ public: @@ -616,13 +609,6 @@ public:
616 BR_PROPERTY(int, blockSize, parallelism * ((sizeof(void*) == 4) ? 128 : 1024)) 609 BR_PROPERTY(int, blockSize, parallelism * ((sizeof(void*) == 4) ? 128 : 1024))
617 610
618 /*! 611 /*!
619 - * \brief true if backProject should be used instead of project (the algorithm should be inverted)  
620 - */  
621 - Q_PROPERTY(bool backProject READ get_backProject WRITE set_backProject RESET reset_backProject)  
622 - BR_PROPERTY(bool, backProject, false)  
623 -  
624 -  
625 - /*!  
626 * \brief If \c true no messages will be sent to the terminal, \c false by default. 612 * \brief If \c true no messages will be sent to the terminal, \c false by default.
627 */ 613 */
628 Q_PROPERTY(bool quiet READ get_quiet WRITE set_quiet RESET reset_quiet) 614 Q_PROPERTY(bool quiet READ get_quiet WRITE set_quiet RESET reset_quiet)
@@ -1048,6 +1034,10 @@ private: @@ -1048,6 +1034,10 @@ private:
1048 * @{ 1034 * @{
1049 */ 1035 */
1050 1036
  1037 +/*!
  1038 + * \brief For asynchronous events during template projection.
  1039 + * \see #Transform::getEvent
  1040 + */
1051 class TemplateEvent : public QObject 1041 class TemplateEvent : public QObject
1052 { 1042 {
1053 Q_OBJECT 1043 Q_OBJECT
@@ -1062,7 +1052,6 @@ signals: @@ -1062,7 +1052,6 @@ signals:
1062 void theSignal(const Template & output) const; 1052 void theSignal(const Template & output) const;
1063 }; 1053 };
1064 1054
1065 -  
1066 /*! 1055 /*!
1067 * \brief Plugin base class for processing a template. 1056 * \brief Plugin base class for processing a template.
1068 * 1057 *
@@ -1076,12 +1065,6 @@ class BR_EXPORT Transform : public Object @@ -1076,12 +1065,6 @@ class BR_EXPORT Transform : public Object
1076 Q_OBJECT 1065 Q_OBJECT
1077 1066
1078 public: 1067 public:
1079 - Q_PROPERTY(int classes READ get_classes WRITE set_classes RESET reset_classes STORED false)  
1080 - Q_PROPERTY(int instances READ get_instances WRITE set_instances RESET reset_instances STORED false)  
1081 - Q_PROPERTY(float fraction READ get_fraction WRITE set_fraction RESET reset_fraction STORED false)  
1082 - BR_PROPERTY(int, classes, std::numeric_limits<int>::max())  
1083 - BR_PROPERTY(int, instances, std::numeric_limits<int>::max())  
1084 - BR_PROPERTY(float, fraction, 1)  
1085 bool independent, trainable; 1068 bool independent, trainable;
1086 1069
1087 virtual ~Transform() {} 1070 virtual ~Transform() {}
@@ -1092,8 +1075,6 @@ public: @@ -1092,8 +1075,6 @@ public:
1092 virtual void train(const TemplateList &data) = 0; /*!< \brief Train the transform. */ 1075 virtual void train(const TemplateList &data) = 0; /*!< \brief Train the transform. */
1093 virtual void project(const Template &src, Template &dst) const = 0; /*!< \brief Apply the transform. */ 1076 virtual void project(const Template &src, Template &dst) const = 0; /*!< \brief Apply the transform. */
1094 virtual void project(const TemplateList &src, TemplateList &dst) const; /*!< \brief Apply the transform. */ 1077 virtual void project(const TemplateList &src, TemplateList &dst) const; /*!< \brief Apply the transform. */
1095 - virtual void backProject(const Template &dst, Template &src) const { src = dst; } /*!< \brief Invert the transform. */  
1096 - virtual void backProject(const TemplateList &dst, TemplateList &src) const; /*!< \brief Invert the transform. */  
1097 1078
1098 /*!< \brief Apply the transform, may update the transform's internal state */ 1079 /*!< \brief Apply the transform, may update the transform's internal state */
1099 virtual void projectUpdate(const Template &src, Template &dst) 1080 virtual void projectUpdate(const Template &src, Template &dst)
openbr/plugins/algorithms.cpp
@@ -46,9 +46,12 @@ class AlgorithmsInitializer : public Initializer @@ -46,9 +46,12 @@ class AlgorithmsInitializer : public Initializer
46 Globals->abbreviations.insert("CropFace", "Open+Cvt(Gray)+Cascade(FrontalFace)+ASEFEyes+Affine(128,128,0.25,0.35)"); 46 Globals->abbreviations.insert("CropFace", "Open+Cvt(Gray)+Cascade(FrontalFace)+ASEFEyes+Affine(128,128,0.25,0.35)");
47 47
48 // Video 48 // Video
49 - Globals->abbreviations.insert("DisplayVideo", "Stream([Show(false,[FrameNumber])+Discard])"); 49 + Globals->abbreviations.insert("DisplayVideo", "Stream([FPSLimit(30)+Show(false,[FrameNumber])+Discard])");
50 Globals->abbreviations.insert("PerFrameDetection", "Stream([SaveMat(original)+Cvt(Gray)+Cascade(FrontalFace)+ASEFEyes+RestoreMat(original)+Draw(inPlace=true),Show(false,[FrameNumber])+Discard])"); 50 Globals->abbreviations.insert("PerFrameDetection", "Stream([SaveMat(original)+Cvt(Gray)+Cascade(FrontalFace)+ASEFEyes+RestoreMat(original)+Draw(inPlace=true),Show(false,[FrameNumber])+Discard])");
51 - Globals->abbreviations.insert("AgeGenderDemo", "Stream([SaveMat(original)+Cvt(Gray)+Cascade(FrontalFace)+Expand+<FaceClassificationRegistration>+<FaceClassificationExtraction>+(<AgeRegressor>+Rename(Subject,Age)+Discard)/(<GenderClassifier>+Rename(Subject,Gender)+Discard)+RestoreMat(original)+Draw(inPlace=true)+DrawPropertiesPoint([Age,Gender],Affine_0,inPlace=true)+SaveMat(original)+Discard+Contract,RestoreMat(original)+FPSCalc+Show(false,[AvgFPS,Age,Gender])+Discard])"); 51 + Globals->abbreviations.insert("AgeGenderDemo", "Stream([SaveMat(original)+Cvt(Gray)+Cascade(FrontalFace)+Expand+<FaceClassificationRegistration>+<FaceClassificationExtraction>+<AgeRegressor>/<GenderClassifier>+Discard+RestoreMat(original)+Draw(inPlace=true)+DrawPropertiesPoint([Age,Gender],Affine_0,inPlace=true)+SaveMat(original)+Discard+Contract,RestoreMat(original)+FPSCalc+Show(false,[AvgFPS,Age,Gender])+Discard])");
  52 + Globals->abbreviations.insert("HOG", "Stream([KeyPointDetector(SIFT)+ROI+Expand+Resize(32,32)+Gradient+RectRegions+Bin(0,360,8)+Hist(8)+Cat])+Contract+CatRows+KMeans(500)+Hist(500)+SVM");
  53 + Globals->abbreviations.insert("HOF", "Stream([KeyPointDetector(SIFT),AggregateFrames(2)+OpticalFlow+Gradient+Bin(0,360,8)+ROI+Hist(8)])+Contract+CatRows+KMeans(500)+Hist(500)");
  54 + Globals->abbreviations.insert("HOGHOF", "Stream([Cvt(Gray),KeyPointDetector(SIFT),AggregateFrames(2),(OpticalFlow+Gradient+Bin(0,360,8)+ROI+Hist(8))/(First+Gradient+Bin(0,360,8)+ROI+Hist(8)),CatCols])+Contract+CatRows+KMeans(500)+Hist(500)");
52 55
53 // Generic Image Processing 56 // Generic Image Processing
54 Globals->abbreviations.insert("SIFT", "Open+KeyPointDetector(SIFT)+KeyPointDescriptor(SIFT):KeyPointMatcher(BruteForce)"); 57 Globals->abbreviations.insert("SIFT", "Open+KeyPointDetector(SIFT)+KeyPointDescriptor(SIFT):KeyPointMatcher(BruteForce)");
@@ -74,14 +77,14 @@ class AlgorithmsInitializer : public Initializer @@ -74,14 +77,14 @@ class AlgorithmsInitializer : public Initializer
74 Globals->abbreviations.insert("FaceDetection", "(Open+Cvt(Gray)+Cascade(FrontalFace))"); 77 Globals->abbreviations.insert("FaceDetection", "(Open+Cvt(Gray)+Cascade(FrontalFace))");
75 Globals->abbreviations.insert("DenseLBP", "(Blur(1.1)+Gamma(0.2)+DoG(1,2)+ContrastEq(0.1,10)+LBP(1,2)+RectRegions(8,8,6,6)+Hist(59))"); 78 Globals->abbreviations.insert("DenseLBP", "(Blur(1.1)+Gamma(0.2)+DoG(1,2)+ContrastEq(0.1,10)+LBP(1,2)+RectRegions(8,8,6,6)+Hist(59))");
76 Globals->abbreviations.insert("DenseSIFT", "(Grid(10,10)+SIFTDescriptor(12)+ByRow)"); 79 Globals->abbreviations.insert("DenseSIFT", "(Grid(10,10)+SIFTDescriptor(12)+ByRow)");
77 - Globals->abbreviations.insert("FaceRecognitionRegistration", "(ASEFEyes+Affine(88,88,0.25,0.35)+FTE(DFFS,instances=1))");  
78 - Globals->abbreviations.insert("FaceRecognitionExtraction", "(Mask+DenseSIFT/DenseLBP+PCA(0.95,instances=1)+Normalize(L2)+Cat)");  
79 - Globals->abbreviations.insert("FaceRecognitionEmbedding", "(Dup(12)+RndSubspace(0.05,1)+LDA(0.98,instances=-2)+Cat+PCA(768,instances=1))"); 80 + Globals->abbreviations.insert("FaceRecognitionRegistration", "(ASEFEyes+Affine(88,88,0.25,0.35)+DownsampleTraining(FTE(DFFS),instances=1))");
  81 + Globals->abbreviations.insert("FaceRecognitionExtraction", "(Mask+DenseSIFT/DenseLBP+DownsampleTraining(PCA(0.95),instances=1)+Normalize(L2)+Cat)");
  82 + Globals->abbreviations.insert("FaceRecognitionEmbedding", "(Dup(12)+RndSubspace(0.05,1)+DownsampleTraining(LDA(0.98),instances=-2)+Cat+DownsampleTraining(PCA(768),instances=1))");
80 Globals->abbreviations.insert("FaceRecognitionQuantization", "(Normalize(L1)+Quantize)"); 83 Globals->abbreviations.insert("FaceRecognitionQuantization", "(Normalize(L1)+Quantize)");
81 Globals->abbreviations.insert("FaceClassificationRegistration", "(ASEFEyes+Affine(56,72,0.33,0.45)+FTE(DFFS))"); 84 Globals->abbreviations.insert("FaceClassificationRegistration", "(ASEFEyes+Affine(56,72,0.33,0.45)+FTE(DFFS))");
82 - Globals->abbreviations.insert("FaceClassificationExtraction", "((Grid(7,7)+SIFTDescriptor(8)+ByRow)/DenseLBP+PCA(0.95,instances=-1)+Cat)");  
83 - Globals->abbreviations.insert("AgeRegressor", "Center(Range,instances=-1)+SVM(RBF,EPS_SVR,instances=100)");  
84 - Globals->abbreviations.insert("GenderClassifier", "Center(Range,instances=-1)+SVM(RBF,C_SVC,instances=4000)"); 85 + Globals->abbreviations.insert("FaceClassificationExtraction", "((Grid(7,7)+SIFTDescriptor(8)+ByRow)/DenseLBP+DownsampleTraining(PCA(0.95),instances=-1, inputVariable=Gender)+Cat)");
  86 + Globals->abbreviations.insert("AgeRegressor", "DownsampleTraining(Center(Range),instances=-1, inputVariable=Age)+DownsampleTraining(SVM(RBF,EPS_SVR,inputVariable=Age),instances=100, inputVariable=Age)");
  87 + Globals->abbreviations.insert("GenderClassifier", "DownsampleTraining(Center(Range),instances=-1, inputVariable=Gender)+DownsampleTraining(SVM(RBF,C_SVC,inputVariable=Gender),instances=4000, inputVariable=Gender)");
85 Globals->abbreviations.insert("UCharL1", "Unit(ByteL1)"); 88 Globals->abbreviations.insert("UCharL1", "Unit(ByteL1)");
86 } 89 }
87 }; 90 };
openbr/plugins/cascade.cpp
@@ -49,19 +49,20 @@ private: @@ -49,19 +49,20 @@ private:
49 } 49 }
50 }; 50 };
51 51
52 -  
53 /*! 52 /*!
54 * \ingroup transforms 53 * \ingroup transforms
55 * \brief Wraps OpenCV cascade classifier 54 * \brief Wraps OpenCV cascade classifier
56 * \author Josh Klontz \cite jklontz 55 * \author Josh Klontz \cite jklontz
57 */ 56 */
58 -class CascadeTransform : public UntrainableTransform 57 +class CascadeTransform : public UntrainableMetaTransform
59 { 58 {
60 Q_OBJECT 59 Q_OBJECT
61 Q_PROPERTY(QString model READ get_model WRITE set_model RESET reset_model STORED false) 60 Q_PROPERTY(QString model READ get_model WRITE set_model RESET reset_model STORED false)
62 Q_PROPERTY(int minSize READ get_minSize WRITE set_minSize RESET reset_minSize STORED false) 61 Q_PROPERTY(int minSize READ get_minSize WRITE set_minSize RESET reset_minSize STORED false)
  62 + Q_PROPERTY(bool ROCMode READ get_ROCMode WRITE set_ROCMode RESET reset_ROCMode STORED false)
63 BR_PROPERTY(QString, model, "FrontalFace") 63 BR_PROPERTY(QString, model, "FrontalFace")
64 BR_PROPERTY(int, minSize, 64) 64 BR_PROPERTY(int, minSize, 64)
  65 + BR_PROPERTY(bool, ROCMode, false)
65 66
66 Resource<CascadeClassifier> cascadeResource; 67 Resource<CascadeClassifier> cascadeResource;
67 68
@@ -72,18 +73,54 @@ class CascadeTransform : public UntrainableTransform @@ -72,18 +73,54 @@ class CascadeTransform : public UntrainableTransform
72 73
73 void project(const Template &src, Template &dst) const 74 void project(const Template &src, Template &dst) const
74 { 75 {
  76 + TemplateList temp;
  77 + project(TemplateList() << src, temp);
  78 + if (!temp.isEmpty()) dst = temp.first();
  79 + }
  80 +
  81 + void project(const TemplateList &src, TemplateList &dst) const
  82 + {
75 CascadeClassifier *cascade = cascadeResource.acquire(); 83 CascadeClassifier *cascade = cascadeResource.acquire();
76 - vector<Rect> rects;  
77 - cascade->detectMultiScale(src, rects, 1.2, 5, src.file.get<bool>("enrollAll", false) ? 0 : CV_HAAR_FIND_BIGGEST_OBJECT, Size(minSize, minSize)); 84 + foreach (const Template &t, src) {
  85 + const bool enrollAll = t.file.getBool("enrollAll");
  86 +
  87 + for (int i=0; i<t.size(); i++) {
  88 + const Mat &m = t[i];
  89 + vector<Rect> rects;
  90 + vector<int> rejectLevels;
  91 + vector<double> levelWeights;
  92 + if (ROCMode) cascade->detectMultiScale(m, rects, rejectLevels, levelWeights, 1.2, 5, (enrollAll ? 0 : CV_HAAR_FIND_BIGGEST_OBJECT) | CV_HAAR_SCALE_IMAGE, Size(minSize, minSize), Size(), true);
  93 + else cascade->detectMultiScale(m, rects, 1.2, 5, enrollAll ? 0 : CV_HAAR_FIND_BIGGEST_OBJECT, Size(minSize, minSize));
  94 +
  95 + if (!enrollAll && rects.empty())
  96 + rects.push_back(Rect(0, 0, m.cols, m.rows));
  97 +
  98 + for (size_t j=0; j<rects.size(); j++) {
  99 + Template u(t.file, m);
  100 + if (rejectLevels.size() > j)
  101 + u.file.set("Confidence", rejectLevels[j]*levelWeights[j]);
  102 + const QRectF rect = OpenCVUtils::fromRect(rects[j]);
  103 + u.file.appendRect(rect);
  104 + u.file.set(model, rect);
  105 + dst.append(u);
  106 + }
  107 + }
  108 + }
  109 +
78 cascadeResource.release(cascade); 110 cascadeResource.release(cascade);
  111 + }
79 112
80 - if (!src.file.get<bool>("enrollAll", false) && rects.empty())  
81 - rects.push_back(Rect(0, 0, src.m().cols, src.m().rows)); 113 + // TODO: Remove this code when ready to break binary compatibility
  114 + void store(QDataStream &stream) const
  115 + {
  116 + int size = 1;
  117 + stream << size;
  118 + }
82 119
83 - foreach (const Rect &rect, rects) {  
84 - dst += src;  
85 - dst.file.appendRect(OpenCVUtils::fromRect(rect));  
86 - } 120 + void load(QDataStream &stream)
  121 + {
  122 + int size;
  123 + stream >> size;
87 } 124 }
88 }; 125 };
89 126
openbr/plugins/cluster.cpp
@@ -89,10 +89,14 @@ class KNNTransform : public Transform @@ -89,10 +89,14 @@ class KNNTransform : public Transform
89 Q_PROPERTY(br::Distance *distance READ get_distance WRITE set_distance RESET reset_distance STORED false) 89 Q_PROPERTY(br::Distance *distance READ get_distance WRITE set_distance RESET reset_distance STORED false)
90 Q_PROPERTY(bool weighted READ get_weighted WRITE set_weighted RESET reset_weighted STORED false) 90 Q_PROPERTY(bool weighted READ get_weighted WRITE set_weighted RESET reset_weighted STORED false)
91 Q_PROPERTY(int numSubjects READ get_numSubjects WRITE set_numSubjects RESET reset_numSubjects STORED false) 91 Q_PROPERTY(int numSubjects READ get_numSubjects WRITE set_numSubjects RESET reset_numSubjects STORED false)
  92 + Q_PROPERTY(QString inputVariable READ get_inputVariable WRITE set_inputVariable RESET reset_inputVariable STORED false)
  93 + Q_PROPERTY(QString outputVariable READ get_outputVariable WRITE set_outputVariable RESET reset_outputVariable STORED false)
92 BR_PROPERTY(int, k, 1) 94 BR_PROPERTY(int, k, 1)
93 BR_PROPERTY(br::Distance*, distance, NULL) 95 BR_PROPERTY(br::Distance*, distance, NULL)
94 BR_PROPERTY(bool, weighted, false) 96 BR_PROPERTY(bool, weighted, false)
95 BR_PROPERTY(int, numSubjects, 1) 97 BR_PROPERTY(int, numSubjects, 1)
  98 + BR_PROPERTY(QString, inputVariable, "Label")
  99 + BR_PROPERTY(QString, outputVariable, "KNN")
96 100
97 TemplateList gallery; 101 TemplateList gallery;
98 102
@@ -111,17 +115,17 @@ class KNNTransform : public Transform @@ -111,17 +115,17 @@ class KNNTransform : public Transform
111 QHash<QString, float> votes; 115 QHash<QString, float> votes;
112 const int max = (k < 1) ? sortedScores.size() : std::min(k, sortedScores.size()); 116 const int max = (k < 1) ? sortedScores.size() : std::min(k, sortedScores.size());
113 for (int j=0; j<max; j++) 117 for (int j=0; j<max; j++)
114 - votes[gallery[sortedScores[j].second].file.get<QString>("Subject")] += (weighted ? sortedScores[j].first : 1); 118 + votes[gallery[sortedScores[j].second].file.get<QString>(inputVariable)] += (weighted ? sortedScores[j].first : 1);
115 subjects.append(votes.keys()[votes.values().indexOf(Common::Max(votes.values()))]); 119 subjects.append(votes.keys()[votes.values().indexOf(Common::Max(votes.values()))]);
116 120
117 // Remove subject from consideration 121 // Remove subject from consideration
118 if (subjects.size() < numSubjects) 122 if (subjects.size() < numSubjects)
119 for (int j=sortedScores.size()-1; j>=0; j--) 123 for (int j=sortedScores.size()-1; j>=0; j--)
120 - if (gallery[sortedScores[j].second].file.get<QString>("Subject") == subjects.last()) 124 + if (gallery[sortedScores[j].second].file.get<QString>(inputVariable) == subjects.last())
121 sortedScores.removeAt(j); 125 sortedScores.removeAt(j);
122 } 126 }
123 127
124 - dst.file.set("KNN", subjects.size() > 1 ? "[" + subjects.join(",") + "]" : subjects.first()); 128 + dst.file.set(outputVariable, subjects.size() > 1 ? "[" + subjects.join(",") + "]" : subjects.first());
125 } 129 }
126 130
127 void store(QDataStream &stream) const 131 void store(QDataStream &stream) const
openbr/plugins/eigen3.cpp
@@ -59,21 +59,6 @@ public: @@ -59,21 +59,6 @@ public:
59 PCATransform() : keep(0.95), drop(0), whiten(false) {} 59 PCATransform() : keep(0.95), drop(0), whiten(false) {}
60 60
61 private: 61 private:
62 - /*  
63 - void backProject(const Template &src, Template &dst) const  
64 - {  
65 - const cv::Mat &m = src;  
66 - dst = cv::Mat(originalRows, m.rows*m.cols/originalRows, CV_32FC1);  
67 -  
68 - // Map Eigen into OpenCV  
69 - Eigen::Map<const Eigen::MatrixXf> inMap(m.ptr<float>(), keep, 1);  
70 - Eigen::Map<Eigen::MatrixXf> outMap(dst.m().ptr<float>(), m.rows*m.cols, 1);  
71 -  
72 - // Do projection  
73 - outMap = (eVecs * inMap) + mean;  
74 - }  
75 - */  
76 -  
77 double residualReconstructionError(const Template &src) const 62 double residualReconstructionError(const Template &src) const
78 { 63 {
79 Template proj; 64 Template proj;
@@ -318,10 +303,12 @@ class LDATransform : public Transform @@ -318,10 +303,12 @@ class LDATransform : public Transform
318 Q_PROPERTY(bool pcaWhiten READ get_pcaWhiten WRITE set_pcaWhiten RESET reset_pcaWhiten STORED false) 303 Q_PROPERTY(bool pcaWhiten READ get_pcaWhiten WRITE set_pcaWhiten RESET reset_pcaWhiten STORED false)
319 Q_PROPERTY(int directLDA READ get_directLDA WRITE set_directLDA RESET reset_directLDA STORED false) 304 Q_PROPERTY(int directLDA READ get_directLDA WRITE set_directLDA RESET reset_directLDA STORED false)
320 Q_PROPERTY(float directDrop READ get_directDrop WRITE set_directDrop RESET reset_directDrop STORED false) 305 Q_PROPERTY(float directDrop READ get_directDrop WRITE set_directDrop RESET reset_directDrop STORED false)
  306 + Q_PROPERTY(QString inputVariable READ get_inputVariable WRITE set_inputVariable RESET reset_inputVariable STORED false)
321 BR_PROPERTY(float, pcaKeep, 0.98) 307 BR_PROPERTY(float, pcaKeep, 0.98)
322 BR_PROPERTY(bool, pcaWhiten, false) 308 BR_PROPERTY(bool, pcaWhiten, false)
323 BR_PROPERTY(int, directLDA, 0) 309 BR_PROPERTY(int, directLDA, 0)
324 BR_PROPERTY(float, directDrop, 0.1) 310 BR_PROPERTY(float, directDrop, 0.1)
  311 + BR_PROPERTY(QString, inputVariable, "Label")
325 312
326 int dimsOut; 313 int dimsOut;
327 Eigen::VectorXf mean; 314 Eigen::VectorXf mean;
@@ -330,7 +317,7 @@ class LDATransform : public Transform @@ -330,7 +317,7 @@ class LDATransform : public Transform
330 void train(const TemplateList &_trainingSet) 317 void train(const TemplateList &_trainingSet)
331 { 318 {
332 // creates "Label" 319 // creates "Label"
333 - TemplateList trainingSet = TemplateList::relabel(_trainingSet, "Subject"); 320 + TemplateList trainingSet = TemplateList::relabel(_trainingSet, inputVariable);
334 321
335 int instances = trainingSet.size(); 322 int instances = trainingSet.size();
336 323
openbr/plugins/eyes.cpp
@@ -186,7 +186,6 @@ private: @@ -186,7 +186,6 @@ private:
186 //dst.file.appendPoint(QPointF(second_eye_x, second_eye_y)); 186 //dst.file.appendPoint(QPointF(second_eye_x, second_eye_y));
187 dst.file.set("First_Eye", QPointF(first_eye_x, first_eye_y)); 187 dst.file.set("First_Eye", QPointF(first_eye_x, first_eye_y));
188 dst.file.set("Second_Eye", QPointF(second_eye_x, second_eye_y)); 188 dst.file.set("Second_Eye", QPointF(second_eye_x, second_eye_y));
189 - dst.file.set("Face", QRect(roi.x, roi.y, roi.width, roi.height));  
190 } 189 }
191 }; 190 };
192 191
openbr/plugins/gallery.cpp
@@ -71,7 +71,7 @@ class arffGallery : public Gallery @@ -71,7 +71,7 @@ class arffGallery : public Gallery
71 } 71 }
72 72
73 arffFile.write(qPrintable(OpenCVUtils::matrixToStringList(t).join(','))); 73 arffFile.write(qPrintable(OpenCVUtils::matrixToStringList(t).join(',')));
74 - arffFile.write(qPrintable(",'" + t.file.get<QString>("Subject") + "'\n")); 74 + arffFile.write(qPrintable(",'" + t.file.get<QString>("Label") + "'\n"));
75 } 75 }
76 }; 76 };
77 77
@@ -643,11 +643,16 @@ class dbGallery : public Gallery @@ -643,11 +643,16 @@ class dbGallery : public Gallery
643 query = query.mid(1, query.size()-2); 643 query = query.mid(1, query.size()-2);
644 if (!q.exec(query)) 644 if (!q.exec(query))
645 qFatal("%s.", qPrintable(q.lastError().text())); 645 qFatal("%s.", qPrintable(q.lastError().text()));
  646 +
646 if ((q.record().count() == 0) || (q.record().count() > 3)) 647 if ((q.record().count() == 0) || (q.record().count() > 3))
647 qFatal("Query record expected one to three fields, got %d.", q.record().count()); 648 qFatal("Query record expected one to three fields, got %d.", q.record().count());
648 const bool hasMetadata = (q.record().count() >= 2); 649 const bool hasMetadata = (q.record().count() >= 2);
649 const bool hasFilter = (q.record().count() >= 3); 650 const bool hasFilter = (q.record().count() >= 3);
650 651
  652 + QString labelName = "Label";
  653 + if (q.record().count() >= 2)
  654 + labelName = q.record().fieldName(1);
  655 +
651 // subset = seed:subjectMaxSize:numSubjects:subjectMinSize or 656 // subset = seed:subjectMaxSize:numSubjects:subjectMinSize or
652 // subset = seed:{Metadata,...,Metadata}:numSubjects 657 // subset = seed:{Metadata,...,Metadata}:numSubjects
653 int seed = 0, subjectMaxSize = std::numeric_limits<int>::max(), numSubjects = std::numeric_limits<int>::max(), subjectMinSize = 0; 658 int seed = 0, subjectMaxSize = std::numeric_limits<int>::max(), numSubjects = std::numeric_limits<int>::max(), subjectMinSize = 0;
@@ -673,6 +678,7 @@ class dbGallery : public Gallery @@ -673,6 +678,7 @@ class dbGallery : public Gallery
673 QHash<QString, QList<Entry> > entries; // QHash<Label, QList<Entry> > 678 QHash<QString, QList<Entry> > entries; // QHash<Label, QList<Entry> >
674 while (q.next()) { 679 while (q.next()) {
675 if (hasFilter && (seed >= 0) && (qHash(q.value(2).toString()) % 2 != (uint)seed % 2)) continue; // Ensures training and testing filters don't overlap 680 if (hasFilter && (seed >= 0) && (qHash(q.value(2).toString()) % 2 != (uint)seed % 2)) continue; // Ensures training and testing filters don't overlap
  681 +
676 if (metadataFields.isEmpty()) 682 if (metadataFields.isEmpty())
677 entries[hasMetadata ? q.value(1).toString() : ""].append(QPair<QString,QString>(q.value(0).toString(), hasFilter ? q.value(2).toString() : "")); 683 entries[hasMetadata ? q.value(1).toString() : ""].append(QPair<QString,QString>(q.value(0).toString(), hasFilter ? q.value(2).toString() : ""));
678 else 684 else
@@ -707,8 +713,10 @@ class dbGallery : public Gallery @@ -707,8 +713,10 @@ class dbGallery : public Gallery
707 713
708 if (entryList.size() > subjectMaxSize) 714 if (entryList.size() > subjectMaxSize)
709 std::random_shuffle(entryList.begin(), entryList.end()); 715 std::random_shuffle(entryList.begin(), entryList.end());
710 - foreach (const Entry &entry, entryList.mid(0, subjectMaxSize))  
711 - templates.append(File(entry.first, label)); 716 + foreach (const Entry &entry, entryList.mid(0, subjectMaxSize)) {
  717 + templates.append(File(entry.first));
  718 + templates.last().file.set(labelName, label);
  719 + }
712 numSubjects--; 720 numSubjects--;
713 } 721 }
714 } 722 }
@@ -816,7 +824,7 @@ class statGallery : public Gallery @@ -816,7 +824,7 @@ class statGallery : public Gallery
816 824
817 void write(const Template &t) 825 void write(const Template &t)
818 { 826 {
819 - subjects.insert(t.file.get<QString>("Subject")); 827 + subjects.insert(t.file.get<QString>("Label"));
820 bytes.append(t.bytes()); 828 bytes.append(t.bytes());
821 } 829 }
822 }; 830 };
openbr/plugins/gui.cpp
@@ -52,26 +52,33 @@ QImage toQImage(const Mat &amp;mat) @@ -52,26 +52,33 @@ QImage toQImage(const Mat &amp;mat)
52 return QImage(mat8uc3.data, mat8uc3.cols, mat8uc3.rows, 3*mat8uc3.cols, QImage::Format_RGB888).copy(); 52 return QImage(mat8uc3.data, mat8uc3.cols, mat8uc3.rows, 3*mat8uc3.cols, QImage::Format_RGB888).copy();
53 } 53 }
54 54
55 -  
56 -// Provides slots for manipulating a QLabel, but does not inherit from QWidget.  
57 -// Therefore, it can be moved to the main thread if not created there initially  
58 -// since god forbid you create a QWidget subclass in not the main thread.  
59 -class GUIProxy : public QObject 55 +class DisplayWindow : public QLabel
60 { 56 {
61 Q_OBJECT 57 Q_OBJECT
  58 +
  59 +protected:
62 QMutex lock; 60 QMutex lock;
63 QWaitCondition wait; 61 QWaitCondition wait;
  62 + QPixmap pixmap;
64 63
65 public: 64 public:
66 65
67 - QLabel *window;  
68 - QPixmap pixmap; 66 + DisplayWindow(QWidget * parent = NULL) : QLabel(parent)
  67 + {
  68 + QApplication::instance()->installEventFilter(this);
  69 + }
69 70
70 - GUIProxy() 71 +public slots:
  72 + void showImage(const QPixmap & input)
71 { 73 {
72 - window = NULL; 74 + pixmap = input;
  75 +
  76 + show();
  77 + setPixmap(pixmap);
  78 + setFixedSize(input.size());
73 } 79 }
74 80
  81 +
75 bool eventFilter(QObject * obj, QEvent * event) 82 bool eventFilter(QObject * obj, QEvent * event)
76 { 83 {
77 if (event->type() == QEvent::KeyPress) 84 if (event->type() == QEvent::KeyPress)
@@ -92,41 +99,10 @@ public: @@ -92,41 +99,10 @@ public:
92 99
93 return QList<QPointF>(); 100 return QList<QPointF>();
94 } 101 }
95 -  
96 -public slots:  
97 -  
98 - void showImage(const QPixmap & input)  
99 - {  
100 - pixmap = input;  
101 -  
102 - window->show();  
103 - window->setPixmap(pixmap);  
104 - window->setFixedSize(input.size());  
105 - }  
106 -  
107 - void createWindow()  
108 - {  
109 - delete window;  
110 - QApplication::instance()->removeEventFilter(this);  
111 -  
112 - window = new QLabel();  
113 - window->setVisible(true);  
114 -  
115 - QApplication::instance()->installEventFilter(this);  
116 - Qt::WindowFlags flags = window->windowFlags();  
117 -  
118 - flags = flags & ~Qt::WindowCloseButtonHint;  
119 - window->setWindowFlags(flags);  
120 - }  
121 -  
122 }; 102 };
123 103
124 -class LandmarkProxy : public GUIProxy 104 +class PointMarkingWindow : public DisplayWindow
125 { 105 {
126 - Q_OBJECT  
127 -  
128 -public:  
129 -  
130 bool eventFilter(QObject *obj, QEvent *event) 106 bool eventFilter(QObject *obj, QEvent *event)
131 { 107 {
132 if (event->type() == QEvent::MouseButtonPress) 108 if (event->type() == QEvent::MouseButtonPress)
@@ -144,11 +120,11 @@ public: @@ -144,11 +120,11 @@ public:
144 painter.setBrush(Qt::red); 120 painter.setBrush(Qt::red);
145 foreach(const QPointF &point, points) painter.drawEllipse(point, 4, 4); 121 foreach(const QPointF &point, points) painter.drawEllipse(point, 4, 4);
146 122
147 - window->setPixmap(pixmapBuffer); 123 + setPixmap(pixmapBuffer);
148 124
149 return true; 125 return true;
150 } else { 126 } else {
151 - return GUIProxy::eventFilter(obj, event); 127 + return DisplayWindow::eventFilter(obj, event);
152 } 128 }
153 } 129 }
154 130
@@ -156,14 +132,85 @@ public: @@ -156,14 +132,85 @@ public:
156 { 132 {
157 points.clear(); 133 points.clear();
158 134
159 - GUIProxy::waitForKey(); 135 + DisplayWindow::waitForKey();
160 136
161 return points; 137 return points;
162 } 138 }
163 139
164 private: 140 private:
165 -  
166 QList<QPointF> points; 141 QList<QPointF> points;
  142 +
  143 +
  144 +};
  145 +
  146 +
  147 +// I want a template class that doesn't look like a template class
  148 +class NominalCreation
  149 +{
  150 +public:
  151 + virtual ~NominalCreation() {}
  152 + virtual void creation()=0;
  153 +};
  154 +
  155 +// Putting the template on a subclass means we can maintain a pointer that
  156 +// doesn't include T in its type.
  157 +template<typename T>
  158 +class ActualCreation : public NominalCreation
  159 +{
  160 +public:
  161 + T * basis;
  162 +
  163 + void creation()
  164 + {
  165 + basis = new T();
  166 + }
  167 +};
  168 +
  169 +// We want to create a QLabel subclass on the main thread, but are running in another thread.
  170 +// We cannot move QWidget subclasses to a different thread (obviously that would be crazy), but
  171 +// we can create one of these, and move it to the main thread, and then use it to create the object
  172 +// we want.
  173 +// Additional fact: QObject subclasses cannot be template classes.
  174 +class MainThreadCreator : public QObject
  175 +{
  176 + Q_OBJECT
  177 +public:
  178 +
  179 + MainThreadCreator()
  180 + {
  181 + this->moveToThread(QApplication::instance()->thread());
  182 +
  183 + connect(this, SIGNAL(needCreation()), this, SLOT(createThing()), Qt::BlockingQueuedConnection);
  184 + }
  185 +
  186 + // While this cannot be a template class, it can still have a template method.
  187 + template<typename T>
  188 + T * getItem()
  189 + {
  190 + if (QThread::currentThread() == QApplication::instance()->thread())
  191 + return new T();
  192 +
  193 + ActualCreation<T> * actualWorker;
  194 + actualWorker = new ActualCreation<T> ();
  195 + worker = actualWorker;
  196 +
  197 + emit needCreation();
  198 +
  199 + T * output = actualWorker->basis;
  200 + delete actualWorker;
  201 + return output;
  202 + }
  203 +
  204 + NominalCreation * worker;
  205 +
  206 +signals:
  207 + void needCreation();
  208 +
  209 +public slots:
  210 + void createThing()
  211 + {
  212 + worker->creation();
  213 + }
167 }; 214 };
168 215
169 /*! 216 /*!
@@ -184,14 +231,14 @@ public: @@ -184,14 +231,14 @@ public:
184 231
185 ShowTransform() : TimeVaryingTransform(false, false) 232 ShowTransform() : TimeVaryingTransform(false, false)
186 { 233 {
187 - gui = NULL;  
188 displayBuffer = NULL; 234 displayBuffer = NULL;
  235 + window = NULL;
189 } 236 }
190 237
191 ~ShowTransform() 238 ~ShowTransform()
192 { 239 {
193 - delete gui;  
194 delete displayBuffer; 240 delete displayBuffer;
  241 + delete window;
195 } 242 }
196 243
197 void train(const TemplateList &data) { (void) data; } 244 void train(const TemplateList &data) { (void) data; }
@@ -235,7 +282,7 @@ public: @@ -235,7 +282,7 @@ public:
235 282
236 // Blocking wait for a key-press 283 // Blocking wait for a key-press
237 if (this->waitInput) 284 if (this->waitInput)
238 - gui->waitForKey(); 285 + window->waitForKey();
239 286
240 } 287 }
241 } 288 }
@@ -249,33 +296,36 @@ public: @@ -249,33 +296,36 @@ public:
249 296
250 void init() 297 void init()
251 { 298 {
  299 + initActual<DisplayWindow>();
  300 + }
  301 +
  302 + template<typename WindowType>
  303 + void initActual()
  304 + {
252 if (!Globals->useGui) 305 if (!Globals->useGui)
253 return; 306 return;
254 307
  308 + if (displayBuffer)
  309 + delete displayBuffer;
255 displayBuffer = new QPixmap(); 310 displayBuffer = new QPixmap();
256 311
257 - // Create our GUI proxy  
258 - gui = new GUIProxy();  
259 - // Move it to the main thread, this means signals we send to it will  
260 - // be run in the main thread, which is hopefully in an event loop  
261 - gui->moveToThread(QApplication::instance()->thread());  
262 -  
263 - // Connect our signals to the proxy's slots  
264 - connect(this, SIGNAL(needWindow()), gui, SLOT(createWindow()), Qt::BlockingQueuedConnection);  
265 - connect(this, SIGNAL(updateImage(QPixmap)), gui,SLOT(showImage(QPixmap))); 312 + if (window)
  313 + delete window;
266 314
267 - emit needWindow();  
268 - connect(this, SIGNAL(changeTitle(QString)), gui->window, SLOT(setWindowTitle(QString)));  
269 - connect(this, SIGNAL(hideWindow()), gui->window, SLOT(hide())); 315 + window = creator.getItem<WindowType>();
  316 + // Connect our signals to the window's slots
  317 + connect(this, SIGNAL(updateImage(QPixmap)), window,SLOT(showImage(QPixmap)));
  318 + connect(this, SIGNAL(changeTitle(QString)), window, SLOT(setWindowTitle(QString)));
  319 + connect(this, SIGNAL(hideWindow()), window, SLOT(hide()));
270 } 320 }
271 321
272 protected: 322 protected:
273 - GUIProxy * gui; 323 + MainThreadCreator creator;
  324 + DisplayWindow * window;
274 QImage qImageBuffer; 325 QImage qImageBuffer;
275 QPixmap * displayBuffer; 326 QPixmap * displayBuffer;
276 327
277 signals: 328 signals:
278 - void needWindow();  
279 void updateImage(const QPixmap & input); 329 void updateImage(const QPixmap & input);
280 void changeTitle(const QString & input); 330 void changeTitle(const QString & input);
281 void hideWindow(); 331 void hideWindow();
@@ -312,7 +362,7 @@ public: @@ -312,7 +362,7 @@ public:
312 362
313 // Blocking wait for a key-press 363 // Blocking wait for a key-press
314 if (this->waitInput) { 364 if (this->waitInput) {
315 - QList<QPointF> points = gui->waitForKey(); 365 + QList<QPointF> points = window->waitForKey();
316 if (keys.isEmpty()) dst[i].file.appendPoints(points); 366 if (keys.isEmpty()) dst[i].file.appendPoints(points);
317 else { 367 else {
318 if (keys.size() == points.size()) 368 if (keys.size() == points.size())
@@ -326,24 +376,7 @@ public: @@ -326,24 +376,7 @@ public:
326 376
327 void init() 377 void init()
328 { 378 {
329 - if (!Globals->useGui)  
330 - return;  
331 -  
332 - displayBuffer = new QPixmap();  
333 -  
334 - // Create our GUI proxy  
335 - gui = new LandmarkProxy();  
336 -  
337 - // Move it to the main thread, this means signals we send to it will  
338 - // be run in the main thread, which is hopefully in an event loop  
339 - gui->moveToThread(QApplication::instance()->thread());  
340 -  
341 - // Connect our signals to the proxy's slots  
342 - connect(this, SIGNAL(needWindow()), gui, SLOT(createWindow()), Qt::BlockingQueuedConnection);  
343 - connect(this, SIGNAL(updateImage(QPixmap)), gui,SLOT(showImage(QPixmap)));  
344 -  
345 - emit needWindow();  
346 - connect(this, SIGNAL(hideWindow()), gui->window, SLOT(hide())); 379 + initActual<PointMarkingWindow>();
347 } 380 }
348 }; 381 };
349 382
openbr/plugins/hist.cpp
@@ -52,8 +52,11 @@ class HistTransform : public UntrainableTransform @@ -52,8 +52,11 @@ class HistTransform : public UntrainableTransform
52 int histSize[] = {dims}; 52 int histSize[] = {dims};
53 float range[] = {min, max}; 53 float range[] = {min, max};
54 const float* ranges[] = {range}; 54 const float* ranges[] = {range};
55 - Mat hist;  
56 - calcHist(&mv[i], 1, channels, Mat(), hist, 1, histSize, ranges); 55 + Mat hist, chan = mv[i];
  56 + // calcHist requires F or U, might as well convert just in case
  57 + if (mv[i].depth() != CV_8U || mv[i].depth() == CV_32F)
  58 + mv[i].convertTo(chan, CV_32F);
  59 + calcHist(&chan, 1, channels, Mat(), hist, 1, histSize, ranges);
57 memcpy(m.ptr(i), hist.ptr(), dims * sizeof(float)); 60 memcpy(m.ptr(i), hist.ptr(), dims * sizeof(float));
58 } 61 }
59 62
openbr/plugins/independent.cpp
@@ -9,37 +9,36 @@ using namespace cv; @@ -9,37 +9,36 @@ using namespace cv;
9 namespace br 9 namespace br
10 { 10 {
11 11
12 -static TemplateList Downsample(const TemplateList &templates, const Transform *transform) 12 +static TemplateList Downsample(const TemplateList &templates, int classes, int instances, float fraction, const QString & inputVariable)
13 { 13 {
14 // Return early when no downsampling is required 14 // Return early when no downsampling is required
15 - if ((transform->classes == std::numeric_limits<int>::max()) &&  
16 - (transform->instances == std::numeric_limits<int>::max()) &&  
17 - (transform->fraction >= 1)) 15 + if ((classes == std::numeric_limits<int>::max()) &&
  16 + (instances == std::numeric_limits<int>::max()) &&
  17 + (fraction >= 1))
18 return templates; 18 return templates;
19 19
20 - const bool atLeast = transform->instances < 0;  
21 - const int instances = abs(transform->instances); 20 + const bool atLeast = instances < 0;
  21 + instances = abs(instances);
22 22
23 - QList<QString> allLabels = File::get<QString>(templates, "Subject"); 23 + QList<QString> allLabels = File::get<QString>(templates, inputVariable);
24 QList<QString> uniqueLabels = allLabels.toSet().toList(); 24 QList<QString> uniqueLabels = allLabels.toSet().toList();
25 qSort(uniqueLabels); 25 qSort(uniqueLabels);
26 26
27 - QMap<QString,int> counts = templates.countValues<QString>("Subject", instances != std::numeric_limits<int>::max()); 27 + QMap<QString,int> counts = templates.countValues<QString>(inputVariable, instances != std::numeric_limits<int>::max());
28 28
29 - if ((instances != std::numeric_limits<int>::max()) && (transform->classes != std::numeric_limits<int>::max())) 29 + if ((instances != std::numeric_limits<int>::max()) && (classes != std::numeric_limits<int>::max()))
30 foreach (const QString & label, counts.keys()) 30 foreach (const QString & label, counts.keys())
31 if (counts[label] < instances) 31 if (counts[label] < instances)
32 counts.remove(label); 32 counts.remove(label);
33 33
34 uniqueLabels = counts.keys(); 34 uniqueLabels = counts.keys();
35 - if ((transform->classes != std::numeric_limits<int>::max()) && (uniqueLabels.size() < transform->classes))  
36 - qWarning("Downsample requested %d classes but only %d are available.", transform->classes, uniqueLabels.size()); 35 + if ((classes != std::numeric_limits<int>::max()) && (uniqueLabels.size() < classes))
  36 + qWarning("Downsample requested %d classes but only %d are available.", classes, uniqueLabels.size());
37 37
38 - Common::seedRNG();  
39 QList<QString> selectedLabels = uniqueLabels; 38 QList<QString> selectedLabels = uniqueLabels;
40 - if (transform->classes < uniqueLabels.size()) { 39 + if (classes < uniqueLabels.size()) {
41 std::random_shuffle(selectedLabels.begin(), selectedLabels.end()); 40 std::random_shuffle(selectedLabels.begin(), selectedLabels.end());
42 - selectedLabels = selectedLabels.mid(0, transform->classes); 41 + selectedLabels = selectedLabels.mid(0, classes);
43 } 42 }
44 43
45 TemplateList downsample; 44 TemplateList downsample;
@@ -56,14 +55,45 @@ static TemplateList Downsample(const TemplateList &amp;templates, const Transform *t @@ -56,14 +55,45 @@ static TemplateList Downsample(const TemplateList &amp;templates, const Transform *t
56 downsample.append(templates.value(indices[j])); 55 downsample.append(templates.value(indices[j]));
57 } 56 }
58 57
59 - if (transform->fraction < 1) { 58 + if (fraction < 1) {
60 std::random_shuffle(downsample.begin(), downsample.end()); 59 std::random_shuffle(downsample.begin(), downsample.end());
61 - downsample = downsample.mid(0, downsample.size()*transform->fraction); 60 + downsample = downsample.mid(0, downsample.size()*fraction);
62 } 61 }
63 62
64 return downsample; 63 return downsample;
65 } 64 }
66 65
  66 +class DownsampleTrainingTransform : public Transform
  67 +{
  68 + Q_OBJECT
  69 + Q_PROPERTY(br::Transform* transform READ get_transform WRITE set_transform RESET reset_transform STORED true)
  70 + Q_PROPERTY(int classes READ get_classes WRITE set_classes RESET reset_classes STORED false)
  71 + Q_PROPERTY(int instances READ get_instances WRITE set_instances RESET reset_instances STORED false)
  72 + Q_PROPERTY(float fraction READ get_fraction WRITE set_fraction RESET reset_fraction STORED false)
  73 + Q_PROPERTY(QString inputVariable READ get_inputVariable WRITE set_inputVariable RESET reset_inputVariable STORED false)
  74 + BR_PROPERTY(br::Transform*, transform, NULL)
  75 + BR_PROPERTY(int, classes, std::numeric_limits<int>::max())
  76 + BR_PROPERTY(int, instances, std::numeric_limits<int>::max())
  77 + BR_PROPERTY(float, fraction, 1)
  78 + BR_PROPERTY(QString, inputVariable, "Label")
  79 +
  80 + void project(const Template & src, Template & dst) const
  81 + {
  82 + transform->project(src,dst);
  83 + }
  84 +
  85 +
  86 + void train(const TemplateList &data)
  87 + {
  88 + if (!transform || !transform->trainable)
  89 + return;
  90 +
  91 + TemplateList downsampled = Downsample(data, classes, instances, fraction, inputVariable);
  92 + transform->train(downsampled);
  93 + }
  94 +};
  95 +BR_REGISTER(Transform, DownsampleTrainingTransform)
  96 +
67 /*! 97 /*!
68 * \ingroup transforms 98 * \ingroup transforms
69 * \brief Clones the transform so that it can be applied independently. 99 * \brief Clones the transform so that it can be applied independently.
@@ -124,13 +154,10 @@ class IndependentTransform : public MetaTransform @@ -124,13 +154,10 @@ class IndependentTransform : public MetaTransform
124 while (transforms.size() < templatesList.size()) 154 while (transforms.size() < templatesList.size())
125 transforms.append(transform->clone()); 155 transforms.append(transform->clone());
126 156
127 - for (int i=0; i<templatesList.size(); i++)  
128 - templatesList[i] = Downsample(templatesList[i], transforms[i]);  
129 -  
130 QFutureSynchronizer<void> futures; 157 QFutureSynchronizer<void> futures;
131 for (int i=0; i<templatesList.size(); i++) 158 for (int i=0; i<templatesList.size(); i++)
132 - futures.addFuture(QtConcurrent::run(_train, transforms[i], &templatesList[i]));  
133 - futures.waitForFinished(); 159 + futures.addFuture(QtConcurrent::run(_train, transforms[i], &templatesList[i]));
  160 + futures.waitForFinished();
134 } 161 }
135 162
136 void project(const Template &src, Template &dst) const 163 void project(const Template &src, Template &dst) const
openbr/plugins/keypoint.cpp
@@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
15 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 15 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16 16
17 #include <opencv2/features2d/features2d.hpp> 17 #include <opencv2/features2d/features2d.hpp>
  18 +#include <opencv2/objdetect/objdetect.hpp>
18 #include <opencv2/nonfree/nonfree.hpp> 19 #include <opencv2/nonfree/nonfree.hpp>
19 #include "openbr_internal.h" 20 #include "openbr_internal.h"
20 #include "openbr/core/opencvutils.h" 21 #include "openbr/core/opencvutils.h"
@@ -178,6 +179,33 @@ BR_REGISTER(Transform, SIFTDescriptorTransform) @@ -178,6 +179,33 @@ BR_REGISTER(Transform, SIFTDescriptorTransform)
178 179
179 /*! 180 /*!
180 * \ingroup transforms 181 * \ingroup transforms
  182 + * \brief OpenCV HOGDescriptor wrapper
  183 + * \author Austin Blanton \cite imaus10
  184 + */
  185 +class HoGDescriptorTransform : public UntrainableTransform
  186 +{
  187 + Q_OBJECT
  188 +
  189 + HOGDescriptor hog;
  190 +
  191 + void project(const Template &src, Template &dst) const
  192 + {
  193 + std::vector<float> descriptorVals;
  194 + std::vector<Point> locations;
  195 + Size winStride = Size(0,0);
  196 + Size padding = Size(0,0);
  197 + foreach (const Mat &rect, src) {
  198 + hog.compute(rect, descriptorVals, winStride, padding, locations);
  199 + Mat HoGFeats(descriptorVals, true);
  200 + dst += HoGFeats;
  201 + }
  202 + }
  203 +};
  204 +
  205 +BR_REGISTER(Transform, HoGDescriptorTransform)
  206 +
  207 +/*!
  208 + * \ingroup transforms
181 * \brief Add landmarks to the template in a grid layout 209 * \brief Add landmarks to the template in a grid layout
182 * \author Josh Klontz \cite jklontz 210 * \author Josh Klontz \cite jklontz
183 */ 211 */
openbr/plugins/mask.cpp
@@ -158,6 +158,9 @@ class LargestConvexAreaTransform : public UntrainableTransform @@ -158,6 +158,9 @@ class LargestConvexAreaTransform : public UntrainableTransform
158 { 158 {
159 Q_OBJECT 159 Q_OBJECT
160 160
  161 + Q_PROPERTY(QString outputVariable READ get_outputVariable WRITE set_outputVariable RESET reset_outputVariable STORED false)
  162 + BR_PROPERTY(QString, outputVariable, "Label")
  163 +
161 void project(const Template &src, Template &dst) const 164 void project(const Template &src, Template &dst) const
162 { 165 {
163 std::vector< std::vector<Point> > contours; 166 std::vector< std::vector<Point> > contours;
@@ -171,7 +174,7 @@ class LargestConvexAreaTransform : public UntrainableTransform @@ -171,7 +174,7 @@ class LargestConvexAreaTransform : public UntrainableTransform
171 if (area / hullArea > 0.98) 174 if (area / hullArea > 0.98)
172 maxArea = std::max(maxArea, area); 175 maxArea = std::max(maxArea, area);
173 } 176 }
174 - dst.file.set("Label", maxArea); 177 + dst.file.set(outputVariable, maxArea);
175 } 178 }
176 }; 179 };
177 180
openbr/plugins/meta.cpp
@@ -105,6 +105,24 @@ class PipeTransform : public CompositeTransform @@ -105,6 +105,24 @@ class PipeTransform : public CompositeTransform
105 transforms[i]->train(copy); 105 transforms[i]->train(copy);
106 } 106 }
107 107
  108 + // if the transform is time varying, we can't project it in parallel
  109 + if (transforms[i]->timeVarying()) {
  110 + fprintf(stderr, "\n%s projecting...", qPrintable(transforms[i]->objectName()));
  111 + for (int j=0; j < singleItemLists.size();j++)
  112 + transforms[i]->projectUpdate(singleItemLists[j], singleItemLists[j]);
  113 +
  114 + // advance i since we already projected for this stage.
  115 + i++;
  116 +
  117 + // set up copy again
  118 + copy.clear();
  119 + for (int j=0; j < singleItemLists.size(); j++)
  120 + copy.append(singleItemLists[j]);
  121 +
  122 + // the next stage might be trainable, so continue to evaluate it.
  123 + continue;
  124 + }
  125 +
108 // We project through any subsequent untrainable transforms at once 126 // We project through any subsequent untrainable transforms at once
109 // as a memory optimization in case any of these intermediate 127 // as a memory optimization in case any of these intermediate
110 // transforms allocate a lot of memory (like OpenTransform) 128 // transforms allocate a lot of memory (like OpenTransform)
@@ -112,7 +130,8 @@ class PipeTransform : public CompositeTransform @@ -112,7 +130,8 @@ class PipeTransform : public CompositeTransform
112 // by that transform at once if we can avoid it. 130 // by that transform at once if we can avoid it.
113 int nextTrainableTransform = i+1; 131 int nextTrainableTransform = i+1;
114 while ((nextTrainableTransform < transforms.size()) && 132 while ((nextTrainableTransform < transforms.size()) &&
115 - !transforms[nextTrainableTransform]->trainable) 133 + !transforms[nextTrainableTransform]->trainable &&
  134 + !transforms[nextTrainableTransform]->timeVarying())
116 nextTrainableTransform++; 135 nextTrainableTransform++;
117 136
118 fprintf(stderr, " projecting..."); 137 fprintf(stderr, " projecting...");
@@ -129,26 +148,6 @@ class PipeTransform : public CompositeTransform @@ -129,26 +148,6 @@ class PipeTransform : public CompositeTransform
129 } 148 }
130 } 149 }
131 150
132 - void backProject(const Template &dst, Template &src) const  
133 - {  
134 - // Backprojecting a time-varying transform is probably not going to work.  
135 - if (timeVarying()) qFatal("No backProject defined for time-varying transform");  
136 -  
137 - src = dst;  
138 - // Reverse order in which transforms are processed  
139 - int length = transforms.length();  
140 - for (int i=length-1; i>=0; i--) {  
141 - Transform *f = transforms.at(i);  
142 - try {  
143 - src >> *f;  
144 - } catch (...) {  
145 - qWarning("Exception triggered when processing %s with transform %s", qPrintable(dst.file.flat()), qPrintable(f->objectName()));  
146 - src = Template(src.file);  
147 - src.file.set("FTE", true);  
148 - }  
149 - }  
150 - }  
151 -  
152 void projectUpdate(const Template &src, Template &dst) 151 void projectUpdate(const Template &src, Template &dst)
153 { 152 {
154 dst = src; 153 dst = src;
@@ -306,8 +305,6 @@ class ForkTransform : public CompositeTransform @@ -306,8 +305,6 @@ class ForkTransform : public CompositeTransform
306 futures.waitForFinished(); 305 futures.waitForFinished();
307 } 306 }
308 307
309 - void backProject(const Template &dst, Template &src) const {Transform::backProject(dst, src);}  
310 -  
311 // same as _project, but calls projectUpdate on sub-transforms 308 // same as _project, but calls projectUpdate on sub-transforms
312 void projectupdate(const Template & src, Template & dst) 309 void projectupdate(const Template & src, Template & dst)
313 { 310 {
@@ -645,17 +642,28 @@ public: @@ -645,17 +642,28 @@ public:
645 QList<TemplateList> input_buffer; 642 QList<TemplateList> input_buffer;
646 input_buffer.reserve(src.size()); 643 input_buffer.reserve(src.size());
647 644
  645 + QFutureSynchronizer<void> futures;
  646 +
648 for (int i =0; i < src.size();i++) { 647 for (int i =0; i < src.size();i++) {
649 input_buffer.append(TemplateList()); 648 input_buffer.append(TemplateList());
650 output_buffer.append(TemplateList()); 649 output_buffer.append(TemplateList());
651 } 650 }
652 -  
653 - QFutureSynchronizer<void> futures; 651 + QList<QFuture<void> > temp;
  652 + temp.reserve(src.size());
654 for (int i=0; i<src.size(); i++) { 653 for (int i=0; i<src.size(); i++) {
655 input_buffer[i].append(src[i]); 654 input_buffer[i].append(src[i]);
656 - if (Globals->parallelism) futures.addFuture(QtConcurrent::run(_projectList, transform, &input_buffer[i], &output_buffer[i]));  
657 - else _projectList( transform, &input_buffer[i], &output_buffer[i]); 655 +
  656 + if (Globals->parallelism > 1) temp.append(QtConcurrent::run(_projectList, transform, &input_buffer[i], &output_buffer[i]));
  657 + else _projectList(transform, &input_buffer[i], &output_buffer[i]);
658 } 658 }
  659 + // We add the futures in reverse order, since in Qt 5.1 at least the
  660 + // waiting thread will wait on them in the order added (which for uniform priority
  661 + // threads is the order of execution), and we want the waiting thread to go in the opposite order
  662 + // so that it can steal runnables and do something besides wait.
  663 + for (int i = temp.size() - 1; i >= 0; i--) {
  664 + futures.addFuture(temp[i]);
  665 + }
  666 +
659 futures.waitForFinished(); 667 futures.waitForFinished();
660 668
661 for (int i=0; i<src.size(); i++) dst.append(output_buffer[i]); 669 for (int i=0; i<src.size(); i++) dst.append(output_buffer[i]);
openbr/plugins/misc.cpp
@@ -179,6 +179,10 @@ class FirstTransform : public UntrainableMetaTransform @@ -179,6 +179,10 @@ class FirstTransform : public UntrainableMetaTransform
179 179
180 void project(const Template &src, Template &dst) const 180 void project(const Template &src, Template &dst) const
181 { 181 {
  182 + // AggregateFrames will leave the Template empty
  183 + // if it hasn't filled up the buffer
  184 + // so we gotta anticipate an empty Template
  185 + if (src.empty()) return;
182 dst.file = src.file; 186 dst.file = src.file;
183 dst = src.m(); 187 dst = src.m();
184 } 188 }
openbr/plugins/normalize.cpp
@@ -97,6 +97,7 @@ class CenterTransform : public Transform @@ -97,6 +97,7 @@ class CenterTransform : public Transform
97 Q_OBJECT 97 Q_OBJECT
98 Q_ENUMS(Method) 98 Q_ENUMS(Method)
99 Q_PROPERTY(Method method READ get_method WRITE set_method RESET reset_method STORED false) 99 Q_PROPERTY(Method method READ get_method WRITE set_method RESET reset_method STORED false)
  100 + Q_PROPERTY(QString inputVariable READ get_inputVariable WRITE set_inputVariable RESET reset_inputVariable STORED false)
100 101
101 public: 102 public:
102 /*!< */ 103 /*!< */
@@ -107,6 +108,7 @@ public: @@ -107,6 +108,7 @@ public:
107 108
108 private: 109 private:
109 BR_PROPERTY(Method, method, Mean) 110 BR_PROPERTY(Method, method, Mean)
  111 + BR_PROPERTY(QString, inputVariable, "Label")
110 112
111 Mat a, b; // dst = (src - b) / a 113 Mat a, b; // dst = (src - b) / a
112 114
@@ -127,7 +129,7 @@ private: @@ -127,7 +129,7 @@ private:
127 Mat m; 129 Mat m;
128 OpenCVUtils::toMat(data.data()).convertTo(m, CV_64F); 130 OpenCVUtils::toMat(data.data()).convertTo(m, CV_64F);
129 131
130 - const QList<int> labels = data.indexProperty("Subject"); 132 + const QList<int> labels = data.indexProperty(inputVariable);
131 const int dims = m.cols; 133 const int dims = m.cols;
132 134
133 vector<Mat> mv, av, bv; 135 vector<Mat> mv, av, bv;
openbr/plugins/openbr_internal.h
@@ -170,14 +170,8 @@ public: @@ -170,14 +170,8 @@ public:
170 virtual void project(const TemplateList &src, TemplateList &dst) const 170 virtual void project(const TemplateList &src, TemplateList &dst) const
171 { 171 {
172 if (timeVarying()) { 172 if (timeVarying()) {
173 - if (!this->timeInvariantAlias) {  
174 - QMutexLocker lock(&aliasLock);  
175 - CompositeTransform * non_const = const_cast<CompositeTransform *>(this);  
176 - non_const->timeInvariantAlias = non_const->smartCopy();  
177 - non_const->timeInvariantAlias->setParent(non_const);  
178 - lock.unlock();  
179 - }  
180 - timeInvariantAlias->projectUpdate(src,dst); 173 + CompositeTransform * non_const = const_cast<CompositeTransform *>(this);
  174 + non_const->projectUpdate(src,dst);
181 return; 175 return;
182 } 176 }
183 _project(src, dst); 177 _project(src, dst);
@@ -225,10 +219,6 @@ public: @@ -225,10 +219,6 @@ public:
225 } 219 }
226 220
227 output->file = this->file; 221 output->file = this->file;
228 - output->classes = classes;  
229 - output->instances = instances;  
230 - output->fraction = fraction;  
231 -  
232 output->init(); 222 output->init();
233 223
234 return output; 224 return output;
@@ -237,13 +227,10 @@ public: @@ -237,13 +227,10 @@ public:
237 protected: 227 protected:
238 bool isTimeVarying; 228 bool isTimeVarying;
239 229
240 - mutable QMutex aliasLock;  
241 - Transform * timeInvariantAlias;  
242 -  
243 virtual void _project(const Template & src, Template & dst) const = 0; 230 virtual void _project(const Template & src, Template & dst) const = 0;
244 virtual void _project(const TemplateList & src, TemplateList & dst) const = 0; 231 virtual void _project(const TemplateList & src, TemplateList & dst) const = 0;
245 232
246 - CompositeTransform() : TimeVaryingTransform(false) { timeInvariantAlias = NULL; } 233 + CompositeTransform() : TimeVaryingTransform(false) {}
247 }; 234 };
248 235
249 } 236 }
openbr/plugins/opticalflow.cpp 0 โ†’ 100644
  1 +#include <opencv2/video/tracking.hpp>
  2 +#include "openbr_internal.h"
  3 +#include "openbr/core/opencvutils.h"
  4 +
  5 +using namespace cv;
  6 +
  7 +namespace br
  8 +{
  9 +
  10 +/*!
  11 + * \ingroup transforms
  12 + * \brief Gets a one-channel dense optical flow from two images
  13 + * \author Austin Blanton \cite imaus10
  14 + */
  15 +class OpticalFlowTransform : public UntrainableMetaTransform
  16 +{
  17 + Q_OBJECT
  18 + Q_PROPERTY(double pyr_scale READ get_pyr_scale WRITE set_pyr_scale RESET reset_pyr_scale STORED false)
  19 + Q_PROPERTY(int levels READ get_levels WRITE set_levels RESET reset_levels STORED false)
  20 + Q_PROPERTY(int winsize READ get_winsize WRITE set_winsize RESET reset_winsize STORED false)
  21 + Q_PROPERTY(int iterations READ get_iterations WRITE set_iterations RESET reset_iterations STORED false)
  22 + Q_PROPERTY(int poly_n READ get_poly_n WRITE set_poly_n RESET reset_poly_n STORED false)
  23 + Q_PROPERTY(double poly_sigma READ get_poly_sigma WRITE set_poly_sigma RESET reset_poly_sigma STORED false)
  24 + Q_PROPERTY(int flags READ get_flags WRITE set_flags RESET reset_flags STORED false)
  25 + // these defaults are optimized for KTH
  26 + BR_PROPERTY(double, pyr_scale, 0.1)
  27 + BR_PROPERTY(int, levels, 1)
  28 + BR_PROPERTY(int, winsize, 5)
  29 + BR_PROPERTY(int, iterations, 10)
  30 + BR_PROPERTY(int, poly_n, 7)
  31 + BR_PROPERTY(double, poly_sigma, 1.1)
  32 + BR_PROPERTY(int, flags, 0)
  33 +
  34 + void project(const Template &src, Template &dst) const
  35 + {
  36 + // get the two images put there by AggregateFrames
  37 + if (src.size() < 2) return;
  38 + Mat prevImg = src[0], nextImg = src[1], flow, flowOneCh;
  39 + if (src[0].channels() != 1) OpenCVUtils::cvtGray(src[0], prevImg);
  40 + if (src[1].channels() != 1) OpenCVUtils::cvtGray(src[1], nextImg);
  41 + calcOpticalFlowFarneback(prevImg, nextImg, flow, pyr_scale, levels, winsize, iterations, poly_n, poly_sigma, flags);
  42 +
  43 + // the result is two channels
  44 + std::vector<Mat> channels(2);
  45 + split(flow, channels);
  46 + magnitude(channels[0], channels[1], flowOneCh);
  47 +
  48 + dst += flowOneCh;
  49 + dst.file = src.file;
  50 + }
  51 +};
  52 +
  53 +BR_REGISTER(Transform, OpticalFlowTransform)
  54 +
  55 +} // namespace br
  56 +
  57 +#include "opticalflow.moc"
openbr/plugins/output.cpp
@@ -146,8 +146,8 @@ class meltOutput : public MatrixOutput @@ -146,8 +146,8 @@ class meltOutput : public MatrixOutput
146 QStringList lines; 146 QStringList lines;
147 if (file.baseName() != "terminal") lines.append(QString("Query,Target,Mask,Similarity%1").arg(keys)); 147 if (file.baseName() != "terminal") lines.append(QString("Query,Target,Mask,Similarity%1").arg(keys));
148 148
149 - QList<QString> queryLabels = File::get<QString>(queryFiles, "Subject");  
150 - QList<QString> targetLabels = File::get<QString>(targetFiles, "Subject"); 149 + QList<QString> queryLabels = File::get<QString>(queryFiles, "Label");
  150 + QList<QString> targetLabels = File::get<QString>(targetFiles, "Label");
151 151
152 for (int i=0; i<queryFiles.size(); i++) { 152 for (int i=0; i<queryFiles.size(); i++) {
153 for (int j=(selfSimilar ? i+1 : 0); j<targetFiles.size(); j++) { 153 for (int j=(selfSimilar ? i+1 : 0); j<targetFiles.size(); j++) {
@@ -300,7 +300,7 @@ class txtOutput : public MatrixOutput @@ -300,7 +300,7 @@ class txtOutput : public MatrixOutput
300 if (file.isNull() || targetFiles.isEmpty() || queryFiles.isEmpty()) return; 300 if (file.isNull() || targetFiles.isEmpty() || queryFiles.isEmpty()) return;
301 QStringList lines; 301 QStringList lines;
302 foreach (const File &file, queryFiles) 302 foreach (const File &file, queryFiles)
303 - lines.append(file.name + " " + file.get<QString>("Subject")); 303 + lines.append(file.name + " " + file.get<QString>("Label"));
304 QtUtils::writeFile(file, lines); 304 QtUtils::writeFile(file, lines);
305 } 305 }
306 }; 306 };
@@ -433,7 +433,7 @@ class rankOutput : public MatrixOutput @@ -433,7 +433,7 @@ class rankOutput : public MatrixOutput
433 foreach (const Pair &pair, Common::Sort(OpenCVUtils::matrixToVector<float>(data.row(i)), true)) { 433 foreach (const Pair &pair, Common::Sort(OpenCVUtils::matrixToVector<float>(data.row(i)), true)) {
434 if (Globals->crossValidate > 0 ? (targetFiles[pair.second].get<int>("Partition",-1) == queryFiles[i].get<int>("Partition",-1)) : true) { 434 if (Globals->crossValidate > 0 ? (targetFiles[pair.second].get<int>("Partition",-1) == queryFiles[i].get<int>("Partition",-1)) : true) {
435 if (QString(targetFiles[pair.second]) != QString(queryFiles[i])) { 435 if (QString(targetFiles[pair.second]) != QString(queryFiles[i])) {
436 - if (targetFiles[pair.second].get<QString>("Subject") == queryFiles[i].get<QString>("Subject")) { 436 + if (targetFiles[pair.second].get<QString>("Label") == queryFiles[i].get<QString>("Label")) {
437 ranks.append(rank); 437 ranks.append(rank);
438 positions.append(pair.second); 438 positions.append(pair.second);
439 scores.append(pair.first); 439 scores.append(pair.first);
openbr/plugins/quality.cpp
@@ -19,17 +19,20 @@ class ImpostorUniquenessMeasureTransform : public Transform @@ -19,17 +19,20 @@ class ImpostorUniquenessMeasureTransform : public Transform
19 Q_PROPERTY(br::Distance* distance READ get_distance WRITE set_distance RESET reset_distance STORED false) 19 Q_PROPERTY(br::Distance* distance READ get_distance WRITE set_distance RESET reset_distance STORED false)
20 Q_PROPERTY(double mean READ get_mean WRITE set_mean RESET reset_mean) 20 Q_PROPERTY(double mean READ get_mean WRITE set_mean RESET reset_mean)
21 Q_PROPERTY(double stddev READ get_stddev WRITE set_stddev RESET reset_stddev) 21 Q_PROPERTY(double stddev READ get_stddev WRITE set_stddev RESET reset_stddev)
  22 + Q_PROPERTY(QString inputVariable READ get_inputVariable WRITE set_inputVariable RESET reset_inputVariable STORED false)
22 BR_PROPERTY(br::Distance*, distance, Distance::make("Dist(L2)", this)) 23 BR_PROPERTY(br::Distance*, distance, Distance::make("Dist(L2)", this))
23 BR_PROPERTY(double, mean, 0) 24 BR_PROPERTY(double, mean, 0)
24 BR_PROPERTY(double, stddev, 1) 25 BR_PROPERTY(double, stddev, 1)
  26 + BR_PROPERTY(QString, inputVariable, "Label")
  27 +
25 TemplateList impostors; 28 TemplateList impostors;
26 29
27 float calculateIUM(const Template &probe, const TemplateList &gallery) const 30 float calculateIUM(const Template &probe, const TemplateList &gallery) const
28 { 31 {
29 - const QString probeLabel = probe.file.get<QString>("Subject"); 32 + const QString probeLabel = probe.file.get<QString>(inputVariable);
30 TemplateList subset = gallery; 33 TemplateList subset = gallery;
31 for (int j=subset.size()-1; j>=0; j--) 34 for (int j=subset.size()-1; j>=0; j--)
32 - if (subset[j].file.get<QString>("Subject") == probeLabel) 35 + if (subset[j].file.get<QString>(inputVariable) == probeLabel)
33 subset.removeAt(j); 36 subset.removeAt(j);
34 37
35 QList<float> scores = distance->compare(subset, probe); 38 QList<float> scores = distance->compare(subset, probe);
@@ -151,6 +154,7 @@ class MatchProbabilityDistance : public Distance @@ -151,6 +154,7 @@ class MatchProbabilityDistance : public Distance
151 Q_PROPERTY(br::Distance* distance READ get_distance WRITE set_distance RESET reset_distance STORED false) 154 Q_PROPERTY(br::Distance* distance READ get_distance WRITE set_distance RESET reset_distance STORED false)
152 Q_PROPERTY(bool gaussian READ get_gaussian WRITE set_gaussian RESET reset_gaussian STORED false) 155 Q_PROPERTY(bool gaussian READ get_gaussian WRITE set_gaussian RESET reset_gaussian STORED false)
153 Q_PROPERTY(bool crossModality READ get_crossModality WRITE set_crossModality RESET reset_crossModality STORED false) 156 Q_PROPERTY(bool crossModality READ get_crossModality WRITE set_crossModality RESET reset_crossModality STORED false)
  157 + Q_PROPERTY(QString inputVariable READ get_inputVariable WRITE set_inputVariable RESET reset_inputVariable STORED false)
154 158
155 MP mp; 159 MP mp;
156 160
@@ -158,7 +162,7 @@ class MatchProbabilityDistance : public Distance @@ -158,7 +162,7 @@ class MatchProbabilityDistance : public Distance
158 { 162 {
159 distance->train(src); 163 distance->train(src);
160 164
161 - const QList<int> labels = src.indexProperty("Subject"); 165 + const QList<int> labels = src.indexProperty(inputVariable);
162 QScopedPointer<MatrixOutput> matrixOutput(MatrixOutput::make(FileList(src.size()), FileList(src.size()))); 166 QScopedPointer<MatrixOutput> matrixOutput(MatrixOutput::make(FileList(src.size()), FileList(src.size())));
163 distance->compare(src, src, matrixOutput.data()); 167 distance->compare(src, src, matrixOutput.data());
164 168
@@ -201,6 +205,7 @@ protected: @@ -201,6 +205,7 @@ protected:
201 BR_PROPERTY(br::Distance*, distance, make("Dist(L2)")) 205 BR_PROPERTY(br::Distance*, distance, make("Dist(L2)"))
202 BR_PROPERTY(bool, gaussian, true) 206 BR_PROPERTY(bool, gaussian, true)
203 BR_PROPERTY(bool, crossModality, false) 207 BR_PROPERTY(bool, crossModality, false)
  208 + BR_PROPERTY(QString, inputVariable, "Label")
204 }; 209 };
205 210
206 BR_REGISTER(Distance, MatchProbabilityDistance) 211 BR_REGISTER(Distance, MatchProbabilityDistance)
@@ -217,10 +222,12 @@ class HeatMapDistance : public Distance @@ -217,10 +222,12 @@ class HeatMapDistance : public Distance
217 Q_PROPERTY(bool gaussian READ get_gaussian WRITE set_gaussian RESET reset_gaussian STORED false) 222 Q_PROPERTY(bool gaussian READ get_gaussian WRITE set_gaussian RESET reset_gaussian STORED false)
218 Q_PROPERTY(bool crossModality READ get_crossModality WRITE set_crossModality RESET reset_crossModality STORED false) 223 Q_PROPERTY(bool crossModality READ get_crossModality WRITE set_crossModality RESET reset_crossModality STORED false)
219 Q_PROPERTY(int step READ get_step WRITE set_step RESET reset_step STORED false) 224 Q_PROPERTY(int step READ get_step WRITE set_step RESET reset_step STORED false)
  225 + Q_PROPERTY(QString inputVariable READ get_inputVariable WRITE set_inputVariable RESET reset_inputVariable STORED false)
220 BR_PROPERTY(br::Distance*, distance, make("Dist(L2)")) 226 BR_PROPERTY(br::Distance*, distance, make("Dist(L2)"))
221 BR_PROPERTY(bool, gaussian, true) 227 BR_PROPERTY(bool, gaussian, true)
222 BR_PROPERTY(bool, crossModality, false) 228 BR_PROPERTY(bool, crossModality, false)
223 BR_PROPERTY(int, step, 1) 229 BR_PROPERTY(int, step, 1)
  230 + BR_PROPERTY(QString, inputVariable, "Label")
224 231
225 QList<MP> mp; 232 QList<MP> mp;
226 233
@@ -228,7 +235,7 @@ class HeatMapDistance : public Distance @@ -228,7 +235,7 @@ class HeatMapDistance : public Distance
228 { 235 {
229 distance->train(src); 236 distance->train(src);
230 237
231 - const QList<int> labels = src.indexProperty("Subject"); 238 + const QList<int> labels = src.indexProperty(inputVariable);
232 239
233 QList<TemplateList> patches; 240 QList<TemplateList> patches;
234 241
@@ -307,14 +314,16 @@ class UnitDistance : public Distance @@ -307,14 +314,16 @@ class UnitDistance : public Distance
307 Q_PROPERTY(br::Distance *distance READ get_distance WRITE set_distance RESET reset_distance) 314 Q_PROPERTY(br::Distance *distance READ get_distance WRITE set_distance RESET reset_distance)
308 Q_PROPERTY(float a READ get_a WRITE set_a RESET reset_a) 315 Q_PROPERTY(float a READ get_a WRITE set_a RESET reset_a)
309 Q_PROPERTY(float b READ get_b WRITE set_b RESET reset_b) 316 Q_PROPERTY(float b READ get_b WRITE set_b RESET reset_b)
  317 + Q_PROPERTY(QString inputVariable READ get_inputVariable WRITE set_inputVariable RESET reset_inputVariable STORED false)
310 BR_PROPERTY(br::Distance*, distance, make("Dist(L2)")) 318 BR_PROPERTY(br::Distance*, distance, make("Dist(L2)"))
311 BR_PROPERTY(float, a, 1) 319 BR_PROPERTY(float, a, 1)
312 BR_PROPERTY(float, b, 0) 320 BR_PROPERTY(float, b, 0)
  321 + BR_PROPERTY(QString, inputVariable, "Label")
313 322
314 void train(const TemplateList &templates) 323 void train(const TemplateList &templates)
315 { 324 {
316 const TemplateList samples = templates.mid(0, 2000); 325 const TemplateList samples = templates.mid(0, 2000);
317 - const QList<int> sampleLabels = samples.indexProperty("Subject"); 326 + const QList<int> sampleLabels = samples.indexProperty(inputVariable);
318 QScopedPointer<MatrixOutput> matrixOutput(MatrixOutput::make(FileList(samples.size()), FileList(samples.size()))); 327 QScopedPointer<MatrixOutput> matrixOutput(MatrixOutput::make(FileList(samples.size()), FileList(samples.size())));
319 Distance::compare(samples, samples, matrixOutput.data()); 328 Distance::compare(samples, samples, matrixOutput.data());
320 329
openbr/plugins/quantize.cpp
@@ -120,6 +120,10 @@ BR_REGISTER(Transform, HistEqQuantizationTransform) @@ -120,6 +120,10 @@ BR_REGISTER(Transform, HistEqQuantizationTransform)
120 class BayesianQuantizationDistance : public Distance 120 class BayesianQuantizationDistance : public Distance
121 { 121 {
122 Q_OBJECT 122 Q_OBJECT
  123 +
  124 + Q_PROPERTY(QString inputVariable READ get_inputVariable WRITE set_inputVariable RESET reset_inputVariable STORED false)
  125 + BR_PROPERTY(QString, inputVariable, "Label")
  126 +
123 QVector<float> loglikelihoods; 127 QVector<float> loglikelihoods;
124 128
125 static void computeLogLikelihood(const Mat &data, const QList<int> &labels, float *loglikelihood) 129 static void computeLogLikelihood(const Mat &data, const QList<int> &labels, float *loglikelihood)
@@ -150,7 +154,7 @@ class BayesianQuantizationDistance : public Distance @@ -150,7 +154,7 @@ class BayesianQuantizationDistance : public Distance
150 qFatal("Expected sigle matrix templates of type CV_8UC1!"); 154 qFatal("Expected sigle matrix templates of type CV_8UC1!");
151 155
152 const Mat data = OpenCVUtils::toMat(src.data()); 156 const Mat data = OpenCVUtils::toMat(src.data());
153 - const QList<int> templateLabels = src.indexProperty("Subject"); 157 + const QList<int> templateLabels = src.indexProperty(inputVariable);
154 loglikelihoods = QVector<float>(data.cols*256, 0); 158 loglikelihoods = QVector<float>(data.cols*256, 0);
155 159
156 QFutureSynchronizer<void> futures; 160 QFutureSynchronizer<void> futures;
@@ -343,9 +347,11 @@ class ProductQuantizationTransform : public Transform @@ -343,9 +347,11 @@ class ProductQuantizationTransform : public Transform
343 Q_PROPERTY(int n READ get_n WRITE set_n RESET reset_n STORED false) 347 Q_PROPERTY(int n READ get_n WRITE set_n RESET reset_n STORED false)
344 Q_PROPERTY(br::Distance *distance READ get_distance WRITE set_distance RESET reset_distance STORED false) 348 Q_PROPERTY(br::Distance *distance READ get_distance WRITE set_distance RESET reset_distance STORED false)
345 Q_PROPERTY(bool bayesian READ get_bayesian WRITE set_bayesian RESET reset_bayesian STORED false) 349 Q_PROPERTY(bool bayesian READ get_bayesian WRITE set_bayesian RESET reset_bayesian STORED false)
  350 + Q_PROPERTY(QString inputVariable READ get_inputVariable WRITE set_inputVariable RESET reset_inputVariable STORED false)
346 BR_PROPERTY(int, n, 2) 351 BR_PROPERTY(int, n, 2)
347 BR_PROPERTY(br::Distance*, distance, Distance::make("L2", this)) 352 BR_PROPERTY(br::Distance*, distance, Distance::make("L2", this))
348 BR_PROPERTY(bool, bayesian, false) 353 BR_PROPERTY(bool, bayesian, false)
  354 + BR_PROPERTY(QString, inputVariable, "Label")
349 355
350 quint16 index; 356 quint16 index;
351 QList<Mat> centers; 357 QList<Mat> centers;
@@ -474,7 +480,7 @@ private: @@ -474,7 +480,7 @@ private:
474 Mat data = OpenCVUtils::toMat(src.data()); 480 Mat data = OpenCVUtils::toMat(src.data());
475 const int step = getStep(data.cols); 481 const int step = getStep(data.cols);
476 482
477 - const QList<int> labels = src.indexProperty("Subject"); 483 + const QList<int> labels = src.indexProperty(inputVariable);
478 484
479 Mat &lut = ProductQuantizationLUTs[index]; 485 Mat &lut = ProductQuantizationLUTs[index];
480 lut = Mat(getDims(data.cols), 256*(256+1)/2, CV_32FC1); 486 lut = Mat(getDims(data.cols), 256*(256+1)/2, CV_32FC1);
openbr/plugins/quantize2.cpp
@@ -19,6 +19,10 @@ namespace br @@ -19,6 +19,10 @@ namespace br
19 class BayesianQuantizationTransform : public Transform 19 class BayesianQuantizationTransform : public Transform
20 { 20 {
21 Q_OBJECT 21 Q_OBJECT
  22 +
  23 + Q_PROPERTY(QString inputVariable READ get_inputVariable WRITE set_inputVariable RESET reset_inputVariable STORED false)
  24 + BR_PROPERTY(QString, inputVariable, "Label")
  25 +
22 QVector<float> thresholds; 26 QVector<float> thresholds;
23 27
24 static void computeThresholdsRecursive(const QVector<int> &cumulativeGenuines, const QVector<int> &cumulativeImpostors, 28 static void computeThresholdsRecursive(const QVector<int> &cumulativeGenuines, const QVector<int> &cumulativeImpostors,
@@ -77,7 +81,7 @@ class BayesianQuantizationTransform : public Transform @@ -77,7 +81,7 @@ class BayesianQuantizationTransform : public Transform
77 void train(const TemplateList &src) 81 void train(const TemplateList &src)
78 { 82 {
79 const Mat data = OpenCVUtils::toMat(src.data()); 83 const Mat data = OpenCVUtils::toMat(src.data());
80 - const QList<int> labels = src.indexProperty("Subject"); 84 + const QList<int> labels = src.indexProperty(inputVariable);
81 85
82 thresholds = QVector<float>(256*data.cols); 86 thresholds = QVector<float>(256*data.cols);
83 87
openbr/plugins/regions.cpp
@@ -114,7 +114,7 @@ BR_REGISTER(Transform, CatTransform) @@ -114,7 +114,7 @@ BR_REGISTER(Transform, CatTransform)
114 /*! 114 /*!
115 * \ingroup transforms 115 * \ingroup transforms
116 * \brief Concatenates all input matrices by row into a single matrix. 116 * \brief Concatenates all input matrices by row into a single matrix.
117 - * All matricies must have the same row counts. 117 + * All matricies must have the same column counts.
118 * \author Josh Klontz \cite jklontz 118 * \author Josh Klontz \cite jklontz
119 */ 119 */
120 class CatRowsTransform : public UntrainableMetaTransform 120 class CatRowsTransform : public UntrainableMetaTransform
@@ -131,6 +131,31 @@ BR_REGISTER(Transform, CatRowsTransform) @@ -131,6 +131,31 @@ BR_REGISTER(Transform, CatRowsTransform)
131 131
132 /*! 132 /*!
133 * \ingroup transforms 133 * \ingroup transforms
  134 + * \brief Concatenates all input matrices by column into a single matrix.
  135 + * Use after a fork to concatenate two feature matrices by column.
  136 + * \author Austin Blanton \cite imaus10
  137 + */
  138 +class CatColsTransform : public UntrainableMetaTransform
  139 +{
  140 + Q_OBJECT
  141 +
  142 + void project(const Template &src, Template &dst) const
  143 + {
  144 + if (src.empty()) return;
  145 + dst.file = src.file;
  146 + Mat m = OpenCVUtils::toMatByRow(src);
  147 + // right now this just splits src in half and joins them horizontally
  148 + // TODO: add partitions parameter for more than a single split
  149 + Mat first = m.rowRange(Range(0, m.rows/2));
  150 + Mat second = m.rowRange(Range(m.rows/2, m.rows));
  151 + hconcat(first, second, dst);
  152 + }
  153 +};
  154 +
  155 +BR_REGISTER(Transform, CatColsTransform)
  156 +
  157 +/*!
  158 + * \ingroup transforms
134 * \brief Reshape the each matrix to the specified number of rows. 159 * \brief Reshape the each matrix to the specified number of rows.
135 * \author Josh Klontz \cite jklontz 160 * \author Josh Klontz \cite jklontz
136 */ 161 */
openbr/plugins/stream.cpp
@@ -31,8 +31,10 @@ public: @@ -31,8 +31,10 @@ public:
31 virtual ~SharedBuffer() {} 31 virtual ~SharedBuffer() {}
32 32
33 virtual void addItem(FrameData * input)=0; 33 virtual void addItem(FrameData * input)=0;
  34 + virtual void reset()=0;
34 35
35 virtual FrameData * tryGetItem()=0; 36 virtual FrameData * tryGetItem()=0;
  37 + virtual int size()=0;
36 }; 38 };
37 39
38 // for n - 1 boundaries, multiple threads call addItem, the frames are 40 // for n - 1 boundaries, multiple threads call addItem, the frames are
@@ -74,6 +76,21 @@ public: @@ -74,6 +76,21 @@ public:
74 return output; 76 return output;
75 } 77 }
76 78
  79 + virtual int size()
  80 + {
  81 + QMutexLocker lock(&bufferGuard);
  82 + return buffer.size();
  83 + }
  84 + virtual void reset()
  85 + {
  86 + if (size() != 0)
  87 + qDebug("Sequencing buffer has non-zero size during reset!");
  88 +
  89 + QMutexLocker lock(&bufferGuard);
  90 + next_target = 0;
  91 + }
  92 +
  93 +
77 private: 94 private:
78 QMutex bufferGuard; 95 QMutex bufferGuard;
79 int next_target; 96 int next_target;
@@ -95,6 +112,11 @@ public: @@ -95,6 +112,11 @@ public:
95 outputBuffer = &buffer2; 112 outputBuffer = &buffer2;
96 } 113 }
97 114
  115 + int size()
  116 + {
  117 + QReadLocker readLock(&bufferGuard);
  118 + return inputBuffer->size() + outputBuffer->size();
  119 + }
98 120
99 // called from the producer thread 121 // called from the producer thread
100 void addItem(FrameData * input) 122 void addItem(FrameData * input)
@@ -133,6 +155,13 @@ public: @@ -133,6 +155,13 @@ public:
133 return output; 155 return output;
134 } 156 }
135 157
  158 + virtual void reset()
  159 + {
  160 + if (this->size() != 0)
  161 + qDebug("Shared buffer has non-zero size during reset!");
  162 + }
  163 +
  164 +
136 private: 165 private:
137 // The read-write lock. The thread adding to this buffer can add 166 // The read-write lock. The thread adding to this buffer can add
138 // to the current input buffer if it has a read lock. The thread 167 // to the current input buffer if it has a read lock. The thread
@@ -160,9 +189,8 @@ class DataSource @@ -160,9 +189,8 @@ class DataSource
160 public: 189 public:
161 DataSource(int maxFrames=500) 190 DataSource(int maxFrames=500)
162 { 191 {
  192 + // The sequence number of the last frame
163 final_frame = -1; 193 final_frame = -1;
164 - last_issued = -2;  
165 - last_received = -3;  
166 for (int i=0; i < maxFrames;i++) 194 for (int i=0; i < maxFrames;i++)
167 { 195 {
168 allFrames.addItem(new FrameData()); 196 allFrames.addItem(new FrameData());
@@ -181,52 +209,67 @@ public: @@ -181,52 +209,67 @@ public:
181 } 209 }
182 210
183 // non-blocking version of getFrame 211 // non-blocking version of getFrame
184 - FrameData * tryGetFrame() 212 + // Returns a NULL FrameData if too many frames are out, or the
  213 + // data source is broken. Sets last_frame to true iff the FrameData
  214 + // returned is the last valid frame, and the data source is now broken.
  215 + FrameData * tryGetFrame(bool & last_frame)
185 { 216 {
  217 + last_frame = false;
  218 +
  219 + if (is_broken) {
  220 + return NULL;
  221 + }
  222 +
  223 + // Try to get a FrameData from the pool, if we can't it means too many
  224 + // frames are already out, and we will return NULL to indicate failure
186 FrameData * aFrame = allFrames.tryGetItem(); 225 FrameData * aFrame = allFrames.tryGetItem();
187 if (aFrame == NULL) 226 if (aFrame == NULL)
188 return NULL; 227 return NULL;
189 228
190 - aFrame->data.clear();  
191 - aFrame->sequenceNumber = -1;  
192 - 229 + // Try to actually read a frame, if this returns false the data source is broken
193 bool res = getNext(*aFrame); 230 bool res = getNext(*aFrame);
194 231
195 - // The datasource broke.  
196 - if (!res) {  
197 - allFrames.addItem(aFrame);  
198 - 232 + // The datasource broke, update final_frame
  233 + if (!res)
  234 + {
199 QMutexLocker lock(&last_frame_update); 235 QMutexLocker lock(&last_frame_update);
200 - // Did we already receive the last frame?  
201 - final_frame = last_issued; 236 + final_frame = lookAhead.back()->sequenceNumber;
  237 + allFrames.addItem(aFrame);
  238 + }
  239 + else {
  240 + lookAhead.push_back(aFrame);
  241 + }
202 242
203 - // We got the last frame before the data source broke,  
204 - // better pulse lastReturned  
205 - if (final_frame == last_received) {  
206 - lastReturned.wakeAll();  
207 - }  
208 - else if (final_frame < last_received)  
209 - std::cout << "Bad last frame " << final_frame << " but received " << last_received << std::endl; 243 + // we will return the first frame on the lookAhead buffer
  244 + FrameData * rVal = lookAhead.first();
  245 + lookAhead.pop_front();
210 246
211 - return NULL; 247 + // If this is the last frame, say so
  248 + if (rVal->sequenceNumber == final_frame) {
  249 + last_frame = true;
  250 + is_broken = true;
212 } 251 }
213 - last_issued = aFrame->sequenceNumber;  
214 - return aFrame; 252 +
  253 + return rVal;
215 } 254 }
216 255
217 - // Returns true if the frame returned was the last 256 + // Return a frame to the pool, returns true if the frame returned was the last
218 // frame issued, false otherwise 257 // frame issued, false otherwise
219 bool returnFrame(FrameData * inputFrame) 258 bool returnFrame(FrameData * inputFrame)
220 { 259 {
  260 + int frameNumber = inputFrame->sequenceNumber;
  261 +
  262 + inputFrame->data.clear();
  263 + inputFrame->sequenceNumber = -1;
221 allFrames.addItem(inputFrame); 264 allFrames.addItem(inputFrame);
222 265
223 bool rval = false; 266 bool rval = false;
224 267
225 QMutexLocker lock(&last_frame_update); 268 QMutexLocker lock(&last_frame_update);
226 - last_received = inputFrame->sequenceNumber;  
227 269
228 - if (inputFrame->sequenceNumber == final_frame) { 270 + if (frameNumber == final_frame) {
229 // We just received the last frame, better pulse 271 // We just received the last frame, better pulse
  272 + allReturned = true;
230 lastReturned.wakeAll(); 273 lastReturned.wakeAll();
231 rval = true; 274 rval = true;
232 } 275 }
@@ -234,23 +277,88 @@ public: @@ -234,23 +277,88 @@ public:
234 return rval; 277 return rval;
235 } 278 }
236 279
237 - void waitLast() 280 + bool waitLast()
238 { 281 {
239 QMutexLocker lock(&last_frame_update); 282 QMutexLocker lock(&last_frame_update);
240 - lastReturned.wait(&last_frame_update); 283 +
  284 + while (!allReturned)
  285 + {
  286 + // This would be a safer wait if we used a timeout, but
  287 + // theoretically that should never matter.
  288 + lastReturned.wait(&last_frame_update);
  289 + }
  290 + return true;
241 } 291 }
242 292
243 - virtual void close() = 0;  
244 - virtual bool open(Template & output, int start_index=0) = 0;  
245 - virtual bool isOpen() = 0; 293 + bool open(Template & output, int start_index = 0)
  294 + {
  295 + is_broken = false;
  296 + allReturned = false;
  297 +
  298 + // The last frame isn't initialized yet
  299 + final_frame = -1;
  300 + // Start our sequence numbers from the input index
  301 + next_sequence_number = start_index;
  302 +
  303 + // Actually open the data source
  304 + bool open_res = concreteOpen(output);
  305 +
  306 + // We couldn't open the data source
  307 + if (!open_res) {
  308 + is_broken = true;
  309 + return false;
  310 + }
  311 +
  312 + // Try to get a frame from the global pool
  313 + FrameData * firstFrame = allFrames.tryGetItem();
  314 +
  315 + // If this fails, things have gone pretty badly.
  316 + if (firstFrame == NULL) {
  317 + is_broken = true;
  318 + return false;
  319 + }
  320 +
  321 + // Read a frame from the video source
  322 + bool res = getNext(*firstFrame);
246 323
  324 + // the data source broke already, we couldn't even get one frame
  325 + // from it even though it claimed to have opened successfully.
  326 + if (!res) {
  327 + is_broken = true;
  328 + return false;
  329 + }
  330 +
  331 + // We read one frame ahead of the last one returned, this allows
  332 + // us to know which frame is the final frame when we return it.
  333 + lookAhead.append(firstFrame);
  334 + return true;
  335 + }
  336 +
  337 + /*
  338 + * Pure virtual methods
  339 + */
  340 +
  341 + // isOpen doesn't appear to particularly work when used on opencv
  342 + // VideoCaptures, so we don't use it for anything important.
  343 + virtual bool isOpen()=0;
  344 + // Called from open, open the data source specified by the input
  345 + // template, don't worry about setting any of the state variables
  346 + // set in open.
  347 + virtual bool concreteOpen(Template & output) = 0;
  348 + // Get the next frame from the data source, store the results in
  349 + // FrameData (including the actual frame and appropriate sequence
  350 + // number).
247 virtual bool getNext(FrameData & input) = 0; 351 virtual bool getNext(FrameData & input) = 0;
  352 + // close the currently open data source.
  353 + virtual void close() = 0;
248 354
  355 + int next_sequence_number;
249 protected: 356 protected:
250 DoubleBuffer allFrames; 357 DoubleBuffer allFrames;
251 int final_frame; 358 int final_frame;
252 - int last_issued;  
253 - int last_received; 359 + bool is_broken;
  360 + bool allReturned;
  361 + QList<FrameData *> lookAhead;
254 362
255 QWaitCondition lastReturned; 363 QWaitCondition lastReturned;
256 QMutex last_frame_update; 364 QMutex last_frame_update;
@@ -262,14 +370,14 @@ class VideoDataSource : public DataSource @@ -262,14 +370,14 @@ class VideoDataSource : public DataSource
262 public: 370 public:
263 VideoDataSource(int maxFrames) : DataSource(maxFrames) {} 371 VideoDataSource(int maxFrames) : DataSource(maxFrames) {}
264 372
265 - bool open(Template &input, int start_index=0) 373 + bool concreteOpen(Template &input)
266 { 374 {
267 - final_frame = -1;  
268 - last_issued = -2;  
269 - last_received = -3;  
270 -  
271 - next_idx = start_index;  
272 basis = input; 375 basis = input;
  376 +
  377 + // We can open either files (well actually this includes addresses of ip cameras
  378 + // through ffmpeg), or webcams. Webcam VideoCaptures are created through a separate
  379 + // overload of open that takes an integer, not a string.
  380 + // So, does this look like an integer?
273 bool is_int = false; 381 bool is_int = false;
274 int anInt = input.file.name.toInt(&is_int); 382 int anInt = input.file.name.toInt(&is_int);
275 if (is_int) 383 if (is_int)
@@ -287,7 +395,8 @@ public: @@ -287,7 +395,8 @@ public:
287 } else { 395 } else {
288 // Yes, we should specify absolute path: 396 // Yes, we should specify absolute path:
289 // http://stackoverflow.com/questions/9396459/loading-a-video-in-opencv-in-python 397 // http://stackoverflow.com/questions/9396459/loading-a-video-in-opencv-in-python
290 - video.open(QFileInfo(input.file.name).absoluteFilePath().toStdString()); 398 + QString fileName = (Globals->path.isEmpty() ? "" : Globals->path + "/") + input.file.name;
  399 + video.open(QFileInfo(fileName).absoluteFilePath().toStdString());
291 } 400 }
292 401
293 return video.isOpened(); 402 return video.isOpened();
@@ -302,29 +411,38 @@ public: @@ -302,29 +411,38 @@ public:
302 private: 411 private:
303 bool getNext(FrameData & output) 412 bool getNext(FrameData & output)
304 { 413 {
305 - if (!isOpen()) 414 + if (!isOpen()) {
  415 + qDebug("video source is not open");
306 return false; 416 return false;
  417 + }
307 418
308 output.data.append(Template(basis.file)); 419 output.data.append(Template(basis.file));
309 - output.data.last().append(cv::Mat()); 420 + output.data.last().m() = cv::Mat();
310 421
311 - output.sequenceNumber = next_idx;  
312 - next_idx++; 422 + output.sequenceNumber = next_sequence_number;
  423 + next_sequence_number++;
313 424
314 - bool res = video.read(output.data.last().last());  
315 - output.data.last().last() = output.data.last().last().clone(); 425 + cv::Mat temp;
  426 + bool res = video.read(temp);
316 427
317 if (!res) { 428 if (!res) {
  429 + // The video capture broke, return false.
  430 + output.data.last().m() = cv::Mat();
318 close(); 431 close();
319 return false; 432 return false;
320 } 433 }
  434 +
  435 + // This clone is critical, if we don't do it then the matrix will
  436 + // be an alias of an internal buffer of the video source, leading
  437 + // to various problems later.
  438 + output.data.last().m() = temp.clone();
  439 +
321 output.data.last().file.set("FrameNumber", output.sequenceNumber); 440 output.data.last().file.set("FrameNumber", output.sequenceNumber);
322 return true; 441 return true;
323 } 442 }
324 443
325 cv::VideoCapture video; 444 cv::VideoCapture video;
326 Template basis; 445 Template basis;
327 - int next_idx;  
328 }; 446 };
329 447
330 // Given a template as input, return its matrices one by one on subsequent calls 448 // Given a template as input, return its matrices one by one on subsequent calls
@@ -334,21 +452,18 @@ class TemplateDataSource : public DataSource @@ -334,21 +452,18 @@ class TemplateDataSource : public DataSource
334 public: 452 public:
335 TemplateDataSource(int maxFrames) : DataSource(maxFrames) 453 TemplateDataSource(int maxFrames) : DataSource(maxFrames)
336 { 454 {
337 - current_idx = INT_MAX; 455 + current_matrix_idx = INT_MAX;
338 data_ok = false; 456 data_ok = false;
339 } 457 }
340 - bool data_ok;  
341 458
342 - bool open(Template &input, int start_index=0) 459 + // To "open" it we just set appropriate indices, we assume that if this
  460 + // is an image, it is already loaded into memory.
  461 + bool concreteOpen(Template &input)
343 { 462 {
344 basis = input; 463 basis = input;
345 - current_idx = 0;  
346 - next_sequence = start_index;  
347 - final_frame = -1;  
348 - last_issued = -2;  
349 - last_received = -3; 464 + current_matrix_idx = 0;
350 465
351 - data_ok = current_idx < basis.size(); 466 + data_ok = current_matrix_idx < basis.size();
352 return data_ok; 467 return data_ok;
353 } 468 }
354 469
@@ -358,39 +473,41 @@ public: @@ -358,39 +473,41 @@ public:
358 473
359 void close() 474 void close()
360 { 475 {
361 - current_idx = INT_MAX; 476 + current_matrix_idx = INT_MAX;
362 basis.clear(); 477 basis.clear();
363 } 478 }
364 479
365 private: 480 private:
366 bool getNext(FrameData & output) 481 bool getNext(FrameData & output)
367 { 482 {
368 - data_ok = current_idx < basis.size(); 483 + data_ok = current_matrix_idx < basis.size();
369 if (!data_ok) 484 if (!data_ok)
370 return false; 485 return false;
371 486
372 - output.data.append(basis[current_idx]);  
373 - current_idx++; 487 + output.data.append(basis[current_matrix_idx]);
  488 + current_matrix_idx++;
374 489
375 - output.sequenceNumber = next_sequence;  
376 - next_sequence++; 490 + output.sequenceNumber = next_sequence_number;
  491 + next_sequence_number++;
377 492
378 output.data.last().file.set("FrameNumber", output.sequenceNumber); 493 output.data.last().file.set("FrameNumber", output.sequenceNumber);
379 return true; 494 return true;
380 } 495 }
381 496
382 Template basis; 497 Template basis;
383 - int current_idx;  
384 - int next_sequence; 498 + // Index of the next matrix to output from the template
  499 + int current_matrix_idx;
  500 +
  501 + // is current_matrix_idx in bounds?
  502 + bool data_ok;
385 }; 503 };
386 504
387 -// Given a template as input, create a VideoDataSource or a TemplateDataSource  
388 -// depending on whether or not it looks like the input template has already  
389 -// loaded frames into memory. 505 +// Given a templatelist as input, create appropriate data source for each
  506 +// individual template
390 class DataSourceManager : public DataSource 507 class DataSourceManager : public DataSource
391 { 508 {
392 public: 509 public:
393 - DataSourceManager() 510 + DataSourceManager(int activeFrames=100) : DataSource(activeFrames)
394 { 511 {
395 actualSource = NULL; 512 actualSource = NULL;
396 } 513 }
@@ -400,6 +517,11 @@ public: @@ -400,6 +517,11 @@ public:
400 close(); 517 close();
401 } 518 }
402 519
  520 + int size()
  521 + {
  522 + return this->allFrames.size();
  523 + }
  524 +
403 void close() 525 void close()
404 { 526 {
405 if (actualSource) { 527 if (actualSource) {
@@ -409,33 +531,40 @@ public: @@ -409,33 +531,40 @@ public:
409 } 531 }
410 } 532 }
411 533
  534 + // We are used through a call to open(TemplateList)
412 bool open(TemplateList & input) 535 bool open(TemplateList & input)
413 { 536 {
414 - currentIdx = 0; 537 + // Set up variables specific to us
  538 + current_template_idx = 0;
415 templates = input; 539 templates = input;
416 540
417 - return open(templates[currentIdx]); 541 + // Call datasourece::open on the first template to set up
  542 + // state variables
  543 + return DataSource::open(templates[current_template_idx]);
418 } 544 }
419 545
420 - bool open(Template & input, int start_index=0) 546 + // Create an actual data source of appropriate type for this template
  547 + // (initially called via the call to DataSource::open, called later
  548 + // as we run out of frames on our templates).
  549 + bool concreteOpen(Template & input)
421 { 550 {
422 close(); 551 close();
423 - final_frame = -1;  
424 - last_issued = -2;  
425 - last_received = -3;  
426 - next_frame = start_index;  
427 552
  553 + bool open_res = false;
428 // Input has no matrices? Its probably a video that hasn't been loaded yet 554 // Input has no matrices? Its probably a video that hasn't been loaded yet
429 if (input.empty()) { 555 if (input.empty()) {
430 actualSource = new VideoDataSource(0); 556 actualSource = new VideoDataSource(0);
431 - actualSource->open(input, next_frame); 557 + open_res = actualSource->concreteOpen(input);
432 } 558 }
  559 + // If the input is not empty, we assume it is a set of frames already
  560 + // in memory.
433 else { 561 else {
434 - // create frame dealer  
435 actualSource = new TemplateDataSource(0); 562 actualSource = new TemplateDataSource(0);
436 - actualSource->open(input, next_frame); 563 + open_res = actualSource->concreteOpen(input);
437 } 564 }
438 - if (!isOpen()) { 565 +
  566 + // The data source failed to open
  567 + if (!open_res) {
439 delete actualSource; 568 delete actualSource;
440 actualSource = NULL; 569 actualSource = NULL;
441 return false; 570 return false;
@@ -446,30 +575,55 @@ public: @@ -446,30 +575,55 @@ public:
446 bool isOpen() { return !actualSource ? false : actualSource->isOpen(); } 575 bool isOpen() { return !actualSource ? false : actualSource->isOpen(); }
447 576
448 protected: 577 protected:
449 - int currentIdx;  
450 - int next_frame; 578 + // Index of the template in the templatelist we are currently reading from
  579 + int current_template_idx;
  580 +
451 TemplateList templates; 581 TemplateList templates;
452 DataSource * actualSource; 582 DataSource * actualSource;
  583 + // Get the next frame, if we run out of frames on the current template
  584 + // move on to the next one.
453 bool getNext(FrameData & output) 585 bool getNext(FrameData & output)
454 { 586 {
455 bool res = actualSource->getNext(output); 587 bool res = actualSource->getNext(output);
  588 + output.sequenceNumber = next_sequence_number;
  589 +
  590 + // OK we got a frame
456 if (res) { 591 if (res) {
457 - next_frame = output.sequenceNumber+1; 592 + // Override the sequence number set by actualSource
  593 + output.data.last().file.set("FrameNumber", output.sequenceNumber);
  594 + next_sequence_number++;
  595 + if (output.data.last().last().empty())
  596 + qDebug("broken matrix");
458 return true; 597 return true;
459 } 598 }
460 599
  600 + // We didn't get a frame, try to move on to the next template.
461 while(!res) { 601 while(!res) {
462 - currentIdx++; 602 + output.data.clear();
  603 + current_template_idx++;
463 604
464 - if (currentIdx >= templates.size()) 605 + // No more templates? We're done
  606 + if (current_template_idx >= templates.size())
465 return false; 607 return false;
466 - bool open_res = open(templates[currentIdx], next_frame); 608 +
  609 + // open the next data source
  610 + bool open_res = concreteOpen(templates[current_template_idx]);
  611 + // We couldn't open it, give up? We could maybe continue here
  612 + // but don't currently.
467 if (!open_res) 613 if (!open_res)
468 return false; 614 return false;
  615 +
  616 + // get a frame from the newly opened data source, if that fails
  617 + // we continue to open the next one.
469 res = actualSource->getNext(output); 618 res = actualSource->getNext(output);
470 } 619 }
  620 + // Finally, set the sequence number for the frame we actually return.
  621 + output.sequenceNumber = next_sequence_number++;
  622 + output.data.last().file.set("FrameNumber", output.sequenceNumber);
  623 +
  624 + if (output.data.last().last().empty())
  625 + qDebug("broken matrix");
471 626
472 - next_frame = output.sequenceNumber+1;  
473 return res; 627 return res;
474 } 628 }
475 629
@@ -477,9 +631,14 @@ protected: @@ -477,9 +631,14 @@ protected:
477 631
478 class ProcessingStage; 632 class ProcessingStage;
479 633
480 -class BasicLoop : public QRunnable 634 +class BasicLoop : public QRunnable, public QFutureInterface<void>
481 { 635 {
482 public: 636 public:
  637 + BasicLoop()
  638 + {
  639 + this->reportStarted();
  640 + }
  641 +
483 void run(); 642 void run();
484 643
485 QList<ProcessingStage *> * stages; 644 QList<ProcessingStage *> * stages;
@@ -506,12 +665,15 @@ public: @@ -506,12 +665,15 @@ public:
506 665
507 virtual void reset()=0; 666 virtual void reset()=0;
508 667
  668 + virtual void status()=0;
  669 +
509 protected: 670 protected:
510 int thread_count; 671 int thread_count;
511 672
512 SharedBuffer * inputBuffer; 673 SharedBuffer * inputBuffer;
513 ProcessingStage * nextStage; 674 ProcessingStage * nextStage;
514 QList<ProcessingStage *> * stages; 675 QList<ProcessingStage *> * stages;
  676 + QThreadPool * threads;
515 Transform * transform; 677 Transform * transform;
516 678
517 }; 679 };
@@ -530,6 +692,7 @@ void BasicLoop::run() @@ -530,6 +692,7 @@ void BasicLoop::run()
530 current_idx++; 692 current_idx++;
531 current_idx = current_idx % stages->size(); 693 current_idx = current_idx % stages->size();
532 } 694 }
  695 + this->reportFinished();
533 } 696 }
534 697
535 class MultiThreadStage : public ProcessingStage 698 class MultiThreadStage : public ProcessingStage
@@ -537,7 +700,8 @@ class MultiThreadStage : public ProcessingStage @@ -537,7 +700,8 @@ class MultiThreadStage : public ProcessingStage
537 public: 700 public:
538 MultiThreadStage(int _input) : ProcessingStage(_input) {} 701 MultiThreadStage(int _input) : ProcessingStage(_input) {}
539 702
540 - 703 + // Not much to worry about here, we will project the input
  704 + // and try to continue to the next stage.
541 FrameData * run(FrameData * input, bool & should_continue) 705 FrameData * run(FrameData * input, bool & should_continue)
542 { 706 {
543 if (input == NULL) { 707 if (input == NULL) {
@@ -551,7 +715,8 @@ public: @@ -551,7 +715,8 @@ public:
551 return input; 715 return input;
552 } 716 }
553 717
554 - // Called from a different thread than run 718 + // Called from a different thread than run. Nothing to worry about
  719 + // we offer no restrictions on when loops may enter this stage.
555 virtual bool tryAcquireNextStage(FrameData *& input) 720 virtual bool tryAcquireNextStage(FrameData *& input)
556 { 721 {
557 (void) input; 722 (void) input;
@@ -562,9 +727,11 @@ public: @@ -562,9 +727,11 @@ public:
562 { 727 {
563 // nothing to do. 728 // nothing to do.
564 } 729 }
  730 + void status(){
  731 + qDebug("multi thread stage %d, nothing to worry about", this->stage_id);
  732 + }
565 }; 733 };
566 734
567 -  
568 class SingleThreadStage : public ProcessingStage 735 class SingleThreadStage : public ProcessingStage
569 { 736 {
570 public: 737 public:
@@ -572,13 +739,18 @@ public: @@ -572,13 +739,18 @@ public:
572 { 739 {
573 currentStatus = STOPPING; 740 currentStatus = STOPPING;
574 next_target = 0; 741 next_target = 0;
  742 + // If the previous stage is single-threaded, queued inputs
  743 + // are stored in a double buffer
575 if (input_variance) { 744 if (input_variance) {
576 this->inputBuffer = new DoubleBuffer(); 745 this->inputBuffer = new DoubleBuffer();
577 } 746 }
  747 + // If it's multi-threaded we need to put the inputs back in order
  748 + // before we can use them, so we use a sequencing buffer.
578 else { 749 else {
579 this->inputBuffer = new SequencingBuffer(); 750 this->inputBuffer = new SequencingBuffer();
580 } 751 }
581 } 752 }
  753 +
582 ~SingleThreadStage() 754 ~SingleThreadStage()
583 { 755 {
584 delete inputBuffer; 756 delete inputBuffer;
@@ -589,6 +761,7 @@ public: @@ -589,6 +761,7 @@ public:
589 QWriteLocker writeLock(&statusLock); 761 QWriteLocker writeLock(&statusLock);
590 currentStatus = STOPPING; 762 currentStatus = STOPPING;
591 next_target = 0; 763 next_target = 0;
  764 + inputBuffer->reset();
592 } 765 }
593 766
594 767
@@ -627,18 +800,26 @@ public: @@ -627,18 +800,26 @@ public:
627 lock.unlock(); 800 lock.unlock();
628 801
629 if (newItem) 802 if (newItem)
630 - {  
631 - BasicLoop * next = new BasicLoop();  
632 - next->stages = stages;  
633 - next->start_idx = this->stage_id;  
634 - next->startItem = newItem;  
635 -  
636 - QThreadPool::globalInstance()->start(next, stages->size() - this->stage_id);  
637 - } 803 + startThread(newItem);
638 804
639 return input; 805 return input;
640 } 806 }
641 807
  808 + void startThread(br::FrameData * newItem)
  809 + {
  810 + BasicLoop * next = new BasicLoop();
  811 + next->stages = stages;
  812 + next->start_idx = this->stage_id;
  813 + next->startItem = newItem;
  814 +
  815 + // We start threads with priority equal to their stage id
  816 + // This is intended to ensure progression, we do queued late stage
  817 + // jobs before queued early stage jobs, and so tend to finish frames
  818 + // rather than go stage by stage. In Qt 5.1, priorities are priorities
  819 + // so we use the stage_id directly.
  820 + this->threads->start(next, stage_id);
  821 + }
  822 +
642 823
643 // Calledfrom a different thread than run. 824 // Calledfrom a different thread than run.
644 bool tryAcquireNextStage(FrameData *& input) 825 bool tryAcquireNextStage(FrameData *& input)
@@ -671,79 +852,109 @@ public: @@ -671,79 +852,109 @@ public:
671 852
672 return true; 853 return true;
673 } 854 }
  855 +
  856 + void status(){
  857 + qDebug("single thread stage %d, status starting? %d, next %d buffer size %d", this->stage_id, this->currentStatus == SingleThreadStage::STARTING, this->next_target, this->inputBuffer->size());
  858 + }
  859 +
674 }; 860 };
675 861
676 -// No input buffer, instead we draw templates from some data source  
677 -// Will be operated by the main thread for the stream 862 +// This stage reads new frames from the data source.
678 class FirstStage : public SingleThreadStage 863 class FirstStage : public SingleThreadStage
679 { 864 {
680 public: 865 public:
681 - FirstStage() : SingleThreadStage(true) {} 866 + FirstStage(int activeFrames = 100) : SingleThreadStage(true), dataSource(activeFrames){ }
682 867
683 DataSourceManager dataSource; 868 DataSourceManager dataSource;
684 869
  870 + void reset()
  871 + {
  872 + dataSource.close();
  873 + SingleThreadStage::reset();
  874 + }
  875 +
685 FrameData * run(FrameData * input, bool & should_continue) 876 FrameData * run(FrameData * input, bool & should_continue)
686 { 877 {
687 - // Is there anything on our input buffer? If so we should start a thread with that.  
688 - QWriteLocker lock(&statusLock);  
689 - input = dataSource.tryGetFrame();  
690 - // Datasource broke?  
691 - if (!input)  
692 - {  
693 - currentStatus = STOPPING;  
694 - should_continue = false;  
695 - return NULL;  
696 - }  
697 - lock.unlock(); 878 + if (input == NULL)
  879 + qFatal("NULL frame in input stage");
698 880
  881 + // Can we enter the next stage?
699 should_continue = nextStage->tryAcquireNextStage(input); 882 should_continue = nextStage->tryAcquireNextStage(input);
700 883
701 - BasicLoop * next = new BasicLoop();  
702 - next->stages = stages;  
703 - next->start_idx = this->stage_id;  
704 - next->startItem = NULL; 884 + // Try to get a frame from the datasource, we keep working on
  885 + // the frame we have, but we will queue another job for the next
  886 + // frame if a frame is currently available.
  887 + QWriteLocker lock(&statusLock);
  888 + bool last_frame = false;
  889 + FrameData * newFrame = dataSource.tryGetFrame(last_frame);
705 890
706 - QThreadPool::globalInstance()->start(next, stages->size() - this->stage_id); 891 + // Were we able to get a frame?
  892 + if (newFrame) startThread(newFrame);
  893 + // If not this stage will enter a stopped state.
  894 + else {
  895 + currentStatus = STOPPING;
  896 + }
  897 +
  898 + lock.unlock();
707 899
708 return input; 900 return input;
709 } 901 }
710 902
711 - // Calledfrom a different thread than run. 903 + // The last stage, trying to access the first stage
712 bool tryAcquireNextStage(FrameData *& input) 904 bool tryAcquireNextStage(FrameData *& input)
713 { 905 {
  906 + // Return the frame, was it the last one?
714 bool was_last = dataSource.returnFrame(input); 907 bool was_last = dataSource.returnFrame(input);
715 input = NULL; 908 input = NULL;
  909 +
  910 + // OK we won't continue.
716 if (was_last) { 911 if (was_last) {
717 return false; 912 return false;
718 } 913 }
719 914
720 - if (!dataSource.isOpen())  
721 - return false;  
722 -  
723 QReadLocker lock(&statusLock); 915 QReadLocker lock(&statusLock);
724 - // Thread is already running, we should just return 916 + // If the first stage is already active we will just end.
725 if (currentStatus == STARTING) 917 if (currentStatus == STARTING)
726 { 918 {
727 return false; 919 return false;
728 } 920 }
729 - // Have to change to a write lock to modify currentStatus 921 +
  922 + // Otherwise we will try to continue, but to do so we have to
  923 + // escalate the lock, and sadly there is no way to do so without
  924 + // releasing the read-mode lock, and getting a new write-mode lock.
730 lock.unlock(); 925 lock.unlock();
731 926
732 QWriteLocker writeLock(&statusLock); 927 QWriteLocker writeLock(&statusLock);
733 - // But someone else might have started a thread in the meantime 928 + // currentStatus might have changed in the gap between releasing the read
  929 + // lock and getting the write lock.
734 if (currentStatus == STARTING) 930 if (currentStatus == STARTING)
735 { 931 {
736 return false; 932 return false;
737 } 933 }
738 - // Ok we'll start a thread 934 +
  935 + bool last_frame = false;
  936 + // Try to get a frame from the data source, if we get one we will
  937 + // continue to the first stage.
  938 + input = dataSource.tryGetFrame(last_frame);
  939 +
  940 + if (!input) {
  941 + return false;
  942 + }
  943 +
739 currentStatus = STARTING; 944 currentStatus = STARTING;
740 945
741 - // We always start a readstage thread with null input, so nothing to do here  
742 return true; 946 return true;
743 } 947 }
744 948
  949 + void status(){
  950 + qDebug("Read stage %d, status starting? %d, next frame %d buffer size %d", this->stage_id, this->currentStatus == SingleThreadStage::STARTING, this->next_target, this->dataSource.size());
  951 + }
  952 +
  953 +
745 }; 954 };
746 955
  956 +// Appened to the end of a Stream's transform sequence. Collects the output
  957 +// from each frame on a single templatelist
747 class LastStage : public SingleThreadStage 958 class LastStage : public SingleThreadStage
748 { 959 {
749 public: 960 public:
@@ -756,6 +967,7 @@ public: @@ -756,6 +967,7 @@ public:
756 private: 967 private:
757 TemplateList collectedOutput; 968 TemplateList collectedOutput;
758 public: 969 public:
  970 +
759 void reset() 971 void reset()
760 { 972 {
761 collectedOutput.clear(); 973 collectedOutput.clear();
@@ -774,11 +986,14 @@ public: @@ -774,11 +986,14 @@ public:
774 } 986 }
775 next_target = input->sequenceNumber + 1; 987 next_target = input->sequenceNumber + 1;
776 988
  989 + // add the item to our output buffer
777 collectedOutput.append(input->data); 990 collectedOutput.append(input->data);
778 991
  992 + // Can we enter the read stage?
779 should_continue = nextStage->tryAcquireNextStage(input); 993 should_continue = nextStage->tryAcquireNextStage(input);
780 994
781 - // Is there anything on our input buffer? If so we should start a thread with that. 995 + // Is there anything on our input buffer? If so we should start a thread
  996 + // in this stage to process that frame.
782 QWriteLocker lock(&statusLock); 997 QWriteLocker lock(&statusLock);
783 FrameData * newItem = inputBuffer->tryGetItem(); 998 FrameData * newItem = inputBuffer->tryGetItem();
784 if (!newItem) 999 if (!newItem)
@@ -788,23 +1003,25 @@ public: @@ -788,23 +1003,25 @@ public:
788 lock.unlock(); 1003 lock.unlock();
789 1004
790 if (newItem) 1005 if (newItem)
791 - {  
792 - BasicLoop * next = new BasicLoop();  
793 - next->stages = stages;  
794 - next->start_idx = this->stage_id;  
795 - next->startItem = newItem;  
796 -  
797 - QThreadPool::globalInstance()->start(next, stages->size() - this->stage_id);  
798 - } 1006 + startThread(newItem);
799 1007
800 return input; 1008 return input;
801 } 1009 }
  1010 +
  1011 + void status(){
  1012 + qDebug("Collection stage %d, status starting? %d, next %d buffer size %d", this->stage_id, this->currentStatus == SingleThreadStage::STARTING, this->next_target, this->inputBuffer->size());
  1013 + }
  1014 +
802 }; 1015 };
803 1016
  1017 +
804 class StreamTransform : public CompositeTransform 1018 class StreamTransform : public CompositeTransform
805 { 1019 {
806 Q_OBJECT 1020 Q_OBJECT
807 public: 1021 public:
  1022 + Q_PROPERTY(int activeFrames READ get_activeFrames WRITE set_activeFrames RESET reset_activeFrames)
  1023 + BR_PROPERTY(int, activeFrames, 100)
  1024 +
808 void train(const TemplateList & data) 1025 void train(const TemplateList & data)
809 { 1026 {
810 foreach(Transform * transform, transforms) { 1027 foreach(Transform * transform, transforms) {
@@ -826,7 +1043,8 @@ public: @@ -826,7 +1043,8 @@ public:
826 qFatal("whatever"); 1043 qFatal("whatever");
827 } 1044 }
828 1045
829 - // start processing 1046 + // start processing, consider all templates in src a continuous
  1047 + // 'video'
830 void projectUpdate(const TemplateList & src, TemplateList & dst) 1048 void projectUpdate(const TemplateList & src, TemplateList & dst)
831 { 1049 {
832 dst = src; 1050 dst = src;
@@ -834,21 +1052,27 @@ public: @@ -834,21 +1052,27 @@ public:
834 bool res = readStage->dataSource.open(dst); 1052 bool res = readStage->dataSource.open(dst);
835 if (!res) return; 1053 if (!res) return;
836 1054
837 - QThreadPool::globalInstance()->releaseThread(); 1055 + // Start the first thread in the stream.
  1056 + QWriteLocker lock(&readStage->statusLock);
838 readStage->currentStatus = SingleThreadStage::STARTING; 1057 readStage->currentStatus = SingleThreadStage::STARTING;
839 1058
840 - BasicLoop loop;  
841 - loop.stages = &this->processingStages;  
842 - loop.start_idx = 0;  
843 - loop.startItem = NULL;  
844 - loop.setAutoDelete(false); 1059 + // We have to get a frame before starting the thread
  1060 + bool last_frame = false;
  1061 + FrameData * firstFrame = readStage->dataSource.tryGetFrame(last_frame);
  1062 + if (firstFrame == NULL)
  1063 + qFatal("Failed to read first frame of video");
845 1064
846 - QThreadPool::globalInstance()->start(&loop, processingStages.size() - processingStages[0]->stage_id); 1065 + readStage->startThread(firstFrame);
  1066 + lock.unlock();
847 1067
848 - // Wait for the end.  
849 - readStage->dataSource.waitLast();  
850 - QThreadPool::globalInstance()->reserveThread(); 1068 + // Wait for the stream to process the last frame available from
  1069 + // the data source.
  1070 + bool wait_res = false;
  1071 + wait_res = readStage->dataSource.waitLast();
851 1072
  1073 + // Now that there are no more incoming frames, call finalize
  1074 + // on each transform in turn to collect any last templates
  1075 + // they wish to issue.
852 TemplateList final_output; 1076 TemplateList final_output;
853 1077
854 // Push finalize through the stages 1078 // Push finalize through the stages
@@ -864,7 +1088,8 @@ public: @@ -864,7 +1088,8 @@ public:
864 final_output.append(output_set); 1088 final_output.append(output_set);
865 } 1089 }
866 1090
867 - // dst is set to all output received by the final stage 1091 + // dst is set to all output received by the final stage, along
  1092 + // with anything output via the calls to finalize.
868 dst = collectionStage->getOutput(); 1093 dst = collectionStage->getOutput();
869 dst.append(final_output); 1094 dst.append(final_output);
870 1095
@@ -876,7 +1101,8 @@ public: @@ -876,7 +1101,8 @@ public:
876 virtual void finalize(TemplateList & output) 1101 virtual void finalize(TemplateList & output)
877 { 1102 {
878 (void) output; 1103 (void) output;
879 - // Not handling this yet -cao 1104 + // Nothing in particular to do here, stream calls finalize
  1105 + // on all child transforms as part of projectUpdate
880 } 1106 }
881 1107
882 // Create and link stages 1108 // Create and link stages
@@ -884,26 +1110,49 @@ public: @@ -884,26 +1110,49 @@ public:
884 { 1110 {
885 if (transforms.isEmpty()) return; 1111 if (transforms.isEmpty()) return;
886 1112
  1113 + // call CompositeTransform::init so that trainable is set
  1114 + // correctly.
  1115 + CompositeTransform::init();
  1116 +
  1117 + // We share a thread pool across streams attached to the same
  1118 + // parent tranform, retrieve or create a thread pool based
  1119 + // on our parent transform.
  1120 + QMutexLocker poolLock(&poolsAccess);
  1121 + QHash<QObject *, QThreadPool *>::Iterator it;
  1122 + if (!pools.contains(this->parent())) {
  1123 + it = pools.insert(this->parent(), new QThreadPool(this));
  1124 + it.value()->setMaxThreadCount(Globals->parallelism);
  1125 + }
  1126 + else it = pools.find(this->parent());
  1127 + threads = it.value();
  1128 + poolLock.unlock();
  1129 +
  1130 + // Are our children time varying or not? This decides whether
  1131 + // we run them in single threaded or multi threaded stages
887 stage_variance.reserve(transforms.size()); 1132 stage_variance.reserve(transforms.size());
888 foreach (const br::Transform *transform, transforms) { 1133 foreach (const br::Transform *transform, transforms) {
889 stage_variance.append(transform->timeVarying()); 1134 stage_variance.append(transform->timeVarying());
890 } 1135 }
891 1136
892 - readStage = new FirstStage(); 1137 + // Additionally, we have a separate stage responsible for reading
  1138 + // frames from the data source
  1139 + readStage = new FirstStage(activeFrames);
893 1140
894 processingStages.push_back(readStage); 1141 processingStages.push_back(readStage);
895 readStage->stage_id = 0; 1142 readStage->stage_id = 0;
896 readStage->stages = &this->processingStages; 1143 readStage->stages = &this->processingStages;
  1144 + readStage->threads = this->threads;
897 1145
  1146 + // Initialize and link a processing stage for each of our child
  1147 + // transforms.
898 int next_stage_id = 1; 1148 int next_stage_id = 1;
899 -  
900 bool prev_stage_variance = true; 1149 bool prev_stage_variance = true;
901 for (int i =0; i < transforms.size(); i++) 1150 for (int i =0; i < transforms.size(); i++)
902 { 1151 {
903 if (stage_variance[i]) 1152 if (stage_variance[i])
904 - { 1153 + // Whether or not the previous stage is multi-threaded controls
  1154 + // the type of input buffer we need in a single threaded stage.
905 processingStages.append(new SingleThreadStage(prev_stage_variance)); 1155 processingStages.append(new SingleThreadStage(prev_stage_variance));
906 - }  
907 else 1156 else
908 processingStages.append(new MultiThreadStage(Globals->parallelism)); 1157 processingStages.append(new MultiThreadStage(Globals->parallelism));
909 1158
@@ -914,24 +1163,31 @@ public: @@ -914,24 +1163,31 @@ public:
914 processingStages[i]->nextStage = processingStages[i+1]; 1163 processingStages[i]->nextStage = processingStages[i+1];
915 1164
916 processingStages.last()->stages = &this->processingStages; 1165 processingStages.last()->stages = &this->processingStages;
  1166 + processingStages.last()->threads = this->threads;
917 1167
918 processingStages.last()->transform = transforms[i]; 1168 processingStages.last()->transform = transforms[i];
919 prev_stage_variance = stage_variance[i]; 1169 prev_stage_variance = stage_variance[i];
920 } 1170 }
921 1171
  1172 + // We also have the last stage, which just puts the output of the
  1173 + // previous stages on a template list.
922 collectionStage = new LastStage(prev_stage_variance); 1174 collectionStage = new LastStage(prev_stage_variance);
923 processingStages.append(collectionStage); 1175 processingStages.append(collectionStage);
924 collectionStage->stage_id = next_stage_id; 1176 collectionStage->stage_id = next_stage_id;
925 collectionStage->stages = &this->processingStages; 1177 collectionStage->stages = &this->processingStages;
  1178 + collectionStage->threads = this->threads;
926 1179
  1180 + // the last transform stage points to collection stage
927 processingStages[processingStages.size() - 2]->nextStage = collectionStage; 1181 processingStages[processingStages.size() - 2]->nextStage = collectionStage;
928 1182
929 - // It's a ring buffer, get it? 1183 + // And the collection stage points to the read stage, because this is
  1184 + // a ring buffer.
930 collectionStage->nextStage = readStage; 1185 collectionStage->nextStage = readStage;
931 } 1186 }
932 1187
933 ~StreamTransform() 1188 ~StreamTransform()
934 { 1189 {
  1190 + // Delete all the stages
935 for (int i = 0; i < processingStages.size(); i++) { 1191 for (int i = 0; i < processingStages.size(); i++) {
936 delete processingStages[i]; 1192 delete processingStages[i];
937 } 1193 }
@@ -945,6 +1201,25 @@ protected: @@ -945,6 +1201,25 @@ protected:
945 1201
946 QList<ProcessingStage *> processingStages; 1202 QList<ProcessingStage *> processingStages;
947 1203
  1204 + // This is a map from parent transforms (of Streams) to thread pools. Rather
  1205 + // than starting threads on the global thread pool, Stream uses separate thread pools
  1206 + // keyed on their parent transform. This is necessary because stream's project starts
  1207 + // threads, then enters an indefinite wait for them to finish. Since we are starting
  1208 + // threads using thread pools, threads themselves are a limited resource. Therefore,
  1209 + // the type of hold and wait done by stream project can lead to deadlock unless
  1210 + // resources are ordered in such a way that a circular wait will not occur. The points
  1211 + // of this hash is to introduce a resource ordering (on threads) that mirrors the structure
  1212 + // of the algorithm. So, as long as the structure of the algorithm is a DAG, the wait done
  1213 + // by stream project will not be circular, since every thread in stream project is waiting
  1214 + // for threads at a lower level to do the work.
  1215 + // This issue doesn't come up in distribute, since a thread waiting on a QFutureSynchronizer
  1216 + // will steal work from those jobs, so in that sense distribute isn't doing a hold and wait.
  1217 + // Waiting for a QFutureSynchronzier isn't really possible here since stream runs an indeteriminate
  1218 + // number of jobs.
  1219 + static QHash<QObject *, QThreadPool *> pools;
  1220 + static QMutex poolsAccess;
  1221 + QThreadPool * threads;
  1222 +
948 void _project(const Template &src, Template &dst) const 1223 void _project(const Template &src, Template &dst) const
949 { 1224 {
950 (void) src; (void) dst; 1225 (void) src; (void) dst;
@@ -957,6 +1232,9 @@ protected: @@ -957,6 +1232,9 @@ protected:
957 } 1232 }
958 }; 1233 };
959 1234
  1235 +QHash<QObject *, QThreadPool *> StreamTransform::pools;
  1236 +QMutex StreamTransform::poolsAccess;
  1237 +
960 BR_REGISTER(Transform, StreamTransform) 1238 BR_REGISTER(Transform, StreamTransform)
961 1239
962 1240
openbr/plugins/svm.cpp
@@ -101,6 +101,8 @@ class SVMTransform : public Transform @@ -101,6 +101,8 @@ class SVMTransform : public Transform
101 Q_PROPERTY(Type type READ get_type WRITE set_type RESET reset_type STORED false) 101 Q_PROPERTY(Type type READ get_type WRITE set_type RESET reset_type STORED false)
102 Q_PROPERTY(float C READ get_C WRITE set_C RESET reset_C STORED false) 102 Q_PROPERTY(float C READ get_C WRITE set_C RESET reset_C STORED false)
103 Q_PROPERTY(float gamma READ get_gamma WRITE set_gamma RESET reset_gamma STORED false) 103 Q_PROPERTY(float gamma READ get_gamma WRITE set_gamma RESET reset_gamma STORED false)
  104 + Q_PROPERTY(QString inputVariable READ get_inputVariable WRITE set_inputVariable RESET reset_inputVariable STORED false)
  105 + Q_PROPERTY(QString outputVariable READ get_outputVariable WRITE set_outputVariable RESET reset_outputVariable STORED false)
104 106
105 public: 107 public:
106 enum Kernel { Linear = CvSVM::LINEAR, 108 enum Kernel { Linear = CvSVM::LINEAR,
@@ -119,6 +121,9 @@ private: @@ -119,6 +121,9 @@ private:
119 BR_PROPERTY(Type, type, C_SVC) 121 BR_PROPERTY(Type, type, C_SVC)
120 BR_PROPERTY(float, C, -1) 122 BR_PROPERTY(float, C, -1)
121 BR_PROPERTY(float, gamma, -1) 123 BR_PROPERTY(float, gamma, -1)
  124 + BR_PROPERTY(QString, inputVariable, "")
  125 + BR_PROPERTY(QString, outputVariable, "")
  126 +
122 127
123 SVM svm; 128 SVM svm;
124 QHash<QString, int> labelMap; 129 QHash<QString, int> labelMap;
@@ -128,14 +133,15 @@ private: @@ -128,14 +133,15 @@ private:
128 { 133 {
129 Mat data = OpenCVUtils::toMat(_data.data()); 134 Mat data = OpenCVUtils::toMat(_data.data());
130 Mat lab; 135 Mat lab;
131 - // If we are doing regression, assume subject has float values 136 + // If we are doing regression, the input variable should have float
  137 + // values
132 if (type == EPS_SVR || type == NU_SVR) { 138 if (type == EPS_SVR || type == NU_SVR) {
133 - lab = OpenCVUtils::toMat(File::get<float>(_data, "Subject")); 139 + lab = OpenCVUtils::toMat(File::get<float>(_data, inputVariable));
134 } 140 }
135 - // If we are doing classification, assume subject has discrete values, map them  
136 - // and store the mapping data 141 + // If we are doing classification, we should be dealing with discrete
  142 + // values. Map them and store the mapping data
137 else { 143 else {
138 - QList<int> dataLabels = _data.indexProperty("Subject", labelMap, reverseLookup); 144 + QList<int> dataLabels = _data.indexProperty(inputVariable, labelMap, reverseLookup);
139 lab = OpenCVUtils::toMat(dataLabels); 145 lab = OpenCVUtils::toMat(dataLabels);
140 } 146 }
141 trainSVM(svm, data, lab, kernel, type, C, gamma); 147 trainSVM(svm, data, lab, kernel, type, C, gamma);
@@ -146,9 +152,9 @@ private: @@ -146,9 +152,9 @@ private:
146 dst = src; 152 dst = src;
147 float prediction = svm.predict(src.m().reshape(1, 1)); 153 float prediction = svm.predict(src.m().reshape(1, 1));
148 if (type == EPS_SVR || type == NU_SVR) 154 if (type == EPS_SVR || type == NU_SVR)
149 - dst.file.set("Subject", prediction); 155 + dst.file.set(outputVariable, prediction);
150 else 156 else
151 - dst.file.set("Subject", reverseLookup[prediction]); 157 + dst.file.set(outputVariable, reverseLookup[prediction]);
152 } 158 }
153 159
154 void store(QDataStream &stream) const 160 void store(QDataStream &stream) const
@@ -162,6 +168,24 @@ private: @@ -162,6 +168,24 @@ private:
162 loadSVM(svm, stream); 168 loadSVM(svm, stream);
163 stream >> labelMap >> reverseLookup; 169 stream >> labelMap >> reverseLookup;
164 } 170 }
  171 +
  172 + void init()
  173 + {
  174 + // Since SVM can do regression or classification, we have to check the problem type before
  175 + // specifying target variable names
  176 + if (inputVariable.isEmpty())
  177 + {
  178 + if (type == EPS_SVR || type == NU_SVR) {
  179 + inputVariable = "Regressor";
  180 + if (outputVariable.isEmpty())
  181 + outputVariable = "Regressand";
  182 + }
  183 + else
  184 + inputVariable = "Label";
  185 + }
  186 + if (outputVariable.isEmpty())
  187 + outputVariable = inputVariable;
  188 + }
165 }; 189 };
166 190
167 BR_REGISTER(Transform, SVMTransform) 191 BR_REGISTER(Transform, SVMTransform)
@@ -178,6 +202,8 @@ class SVMDistance : public Distance @@ -178,6 +202,8 @@ class SVMDistance : public Distance
178 Q_ENUMS(Type) 202 Q_ENUMS(Type)
179 Q_PROPERTY(Kernel kernel READ get_kernel WRITE set_kernel RESET reset_kernel STORED false) 203 Q_PROPERTY(Kernel kernel READ get_kernel WRITE set_kernel RESET reset_kernel STORED false)
180 Q_PROPERTY(Type type READ get_type WRITE set_type RESET reset_type STORED false) 204 Q_PROPERTY(Type type READ get_type WRITE set_type RESET reset_type STORED false)
  205 + Q_PROPERTY(QString inputVariable READ get_inputVariable WRITE set_inputVariable RESET reset_inputVariable STORED false)
  206 +
181 207
182 public: 208 public:
183 enum Kernel { Linear = CvSVM::LINEAR, 209 enum Kernel { Linear = CvSVM::LINEAR,
@@ -194,13 +220,14 @@ public: @@ -194,13 +220,14 @@ public:
194 private: 220 private:
195 BR_PROPERTY(Kernel, kernel, Linear) 221 BR_PROPERTY(Kernel, kernel, Linear)
196 BR_PROPERTY(Type, type, EPS_SVR) 222 BR_PROPERTY(Type, type, EPS_SVR)
  223 + BR_PROPERTY(QString, inputVariable, "Label")
197 224
198 SVM svm; 225 SVM svm;
199 226
200 void train(const TemplateList &src) 227 void train(const TemplateList &src)
201 { 228 {
202 const Mat data = OpenCVUtils::toMat(src.data()); 229 const Mat data = OpenCVUtils::toMat(src.data());
203 - const QList<int> lab = src.indexProperty("Subject"); 230 + const QList<int> lab = src.indexProperty(inputVariable);
204 231
205 const int instances = data.rows * (data.rows+1) / 2; 232 const int instances = data.rows * (data.rows+1) / 2;
206 Mat deltaData(instances, data.cols, data.type()); 233 Mat deltaData(instances, data.cols, data.type());
openbr/plugins/validate.cpp
@@ -159,26 +159,29 @@ class MetadataDistance : public Distance @@ -159,26 +159,29 @@ class MetadataDistance : public Distance
159 float compare(const Template &a, const Template &b) const 159 float compare(const Template &a, const Template &b) const
160 { 160 {
161 foreach (const QString &key, filters) { 161 foreach (const QString &key, filters) {
  162 + QString aValue = a.file.get<QString>(key, QString());
  163 + QString bValue = b.file.get<QString>(key, QString());
162 164
163 - const QString aValue = a.file.get<QString>(key, "");  
164 - const QString bValue = b.file.get<QString>(key, ""); 165 + // The query value may be a range. Let's check.
  166 + if (bValue.isEmpty()) bValue = QtUtils::toString(b.file.get<QPointF>(key, QPointF()));
165 167
166 if (aValue.isEmpty() || bValue.isEmpty()) continue; 168 if (aValue.isEmpty() || bValue.isEmpty()) continue;
167 169
168 bool keep = false; 170 bool keep = false;
  171 + bool ok;
169 172
170 - if (aValue[0] == '(') /* Range */ {  
171 - QStringList values = aValue.split(','); 173 + QPointF range = QtUtils::toPoint(bValue,&ok);
172 174
173 - int value = values[0].mid(1).toInt();  
174 - values[1].chop(1);  
175 - int upperBound = values[1].toInt(); 175 + if (ok) /* Range */ {
  176 + int value = range.x();
  177 + int upperBound = range.y();
176 178
177 while (value <= upperBound) { 179 while (value <= upperBound) {
178 - if (aValue == bValue) { 180 + if (aValue == QString::number(value)) {
179 keep = true; 181 keep = true;
180 break; 182 break;
181 } 183 }
  184 + value++;
182 } 185 }
183 } 186 }
184 else if (aValue == bValue) keep = true; 187 else if (aValue == bValue) keep = true;
scripts/downloadDatasets.sh
@@ -64,6 +64,5 @@ if [ ! -d ../data/KTH/vid ]; then @@ -64,6 +64,5 @@ if [ ! -d ../data/KTH/vid ]; then
64 rm ${vidclass}.zip 64 rm ${vidclass}.zip
65 done 65 done
66 # this file is corrupted 66 # this file is corrupted
67 - chmod +w ../data/KTH/vid/boxing/person01_boxing_d4_uncomp.avi  
68 - rm ../data/KTH/vid/boxing/person01_boxing_d4_uncomp.avi 67 + rm -f ../data/KTH/vid/boxing/person01_boxing_d4_uncomp.avi
69 fi 68 fi
scripts/evalAgeRegression-PCSO.sh
@@ -4,8 +4,12 @@ if [ ! -f evalAgeRegression-PCSO.sh ]; then @@ -4,8 +4,12 @@ if [ ! -f evalAgeRegression-PCSO.sh ]; then
4 exit 4 exit
5 fi 5 fi
6 6
  7 +export BR="../build/app/br/br -useGui 0"
  8 +export PCSO_DIR=/user/pripshare/Databases/FaceDatabases/PCSO/PCSO/
  9 +export ageAlg=AgeRegression
  10 +
7 # Create a file list by querying the database 11 # Create a file list by querying the database
8 -br -quiet -algorithm Identity -enroll "../data/PCSO/PCSO.db[query='SELECT File,Age,PersonID FROM PCSO WHERE Age >= 15 AND AGE <= 75', subset=1:200]" terminal.txt > Input.txt 12 +$BR -quiet -algorithm Identity -enroll "$PCSO_DIR/PCSO.db[query='SELECT File,Age,PersonID FROM PCSO WHERE Age >= 17 AND AGE <= 68', subset=1:200]" terminal.txt > Input.txt
9 13
10 # Enroll the file list and evaluate performance 14 # Enroll the file list and evaluate performance
11 -br -algorithm AgeRegression -path ../data/PCSO/img -enroll Input.txt Output.txt -evalRegression Output.txt Input.txt 15 +$BR -algorithm $ageAlg -path $PCSO_DIR/Images -enroll Input.txt Output.txt -evalRegression Output.txt Input.txt Age
scripts/evalFaceRecognition-MEDS.sh
@@ -20,11 +20,11 @@ if [ ! -e Algorithm_Dataset ]; then @@ -20,11 +20,11 @@ if [ ! -e Algorithm_Dataset ]; then
20 fi 20 fi
21 21
22 if [ ! -e MEDS.mask ]; then 22 if [ ! -e MEDS.mask ]; then
23 - br -makeMask ../data/MEDS/sigset/MEDS_frontal_target.xml ../data/MEDS/sigset/MEDS_frontal_query.xml MEDS.mask 23 + br -useGui 0 -makeMask ../data/MEDS/sigset/MEDS_frontal_target.xml ../data/MEDS/sigset/MEDS_frontal_query.xml MEDS.mask
24 fi 24 fi
25 25
26 # Run Algorithm on MEDS 26 # Run Algorithm on MEDS
27 -br -algorithm ${ALGORITHM} -path ../data/MEDS/img -compare ../data/MEDS/sigset/MEDS_frontal_target.xml ../data/MEDS/sigset/MEDS_frontal_query.xml ${ALGORITHM}_MEDS.mtx -eval ${ALGORITHM}_MEDS.mtx MEDS.mask Algorithm_Dataset/${ALGORITHM}_MEDS.csv 27 +br -useGui 0 -algorithm ${ALGORITHM} -path ../data/MEDS/img -compare ../data/MEDS/sigset/MEDS_frontal_target.xml ../data/MEDS/sigset/MEDS_frontal_query.xml ${ALGORITHM}_MEDS.mtx -eval ${ALGORITHM}_MEDS.mtx MEDS.mask Algorithm_Dataset/${ALGORITHM}_MEDS.csv
28 28
29 # Plot results 29 # Plot results
30 -br -plot Algorithm_Dataset/*_MEDS.csv MEDS 30 +br -useGui 0 -plot Algorithm_Dataset/*_MEDS.csv MEDS
scripts/evalGenderClassification-PCSO.sh
@@ -4,8 +4,13 @@ if [ ! -f evalGenderClassification-PCSO.sh ]; then @@ -4,8 +4,13 @@ if [ ! -f evalGenderClassification-PCSO.sh ]; then
4 exit 4 exit
5 fi 5 fi
6 6
  7 +export BR=../build/app/br/br
  8 +export genderAlg=GenderClassification
  9 +
  10 +export PCSO_DIR=/user/pripshare/Databases/FaceDatabases/PCSO/PCSO/
  11 +
7 # Create a file list by querying the database 12 # Create a file list by querying the database
8 -br -quiet -algorithm Identity -enroll "../data/PCSO/PCSO.db[query='SELECT File,Gender,PersonID FROM PCSO', subset=1:8000]" terminal.txt > Input.txt 13 +$BR -useGui 0 -quiet -algorithm Identity -enroll "$PCSO_DIR/PCSO.db[query='SELECT File,Gender,PersonID FROM PCSO', subset=1:8000]" terminal.txt > Input.txt
9 14
10 # Enroll the file list and evaluate performance 15 # Enroll the file list and evaluate performance
11 -br -algorithm GenderClassification -path ../data/PCSO/img -enroll Input.txt Output.txt -evalClassification Output.txt Input.txt 16 +$BR -useGui 0 -algorithm $genderAlg -path $PCSO_DIR/Images -enroll Input.txt Output.txt -evalClassification Output.txt Input.txt Gender
12 \ No newline at end of file 17 \ No newline at end of file
scripts/trainAgeRegression-PCSO.sh
@@ -6,6 +6,11 @@ fi @@ -6,6 +6,11 @@ fi
6 6
7 #rm -f ../share/openbr/models/features/FaceClassificationRegistration 7 #rm -f ../share/openbr/models/features/FaceClassificationRegistration
8 #rm -f ../share/openbr/models/features/FaceClassificationExtraction 8 #rm -f ../share/openbr/models/features/FaceClassificationExtraction
9 -rm -f ../share/openbr/models/algorithms/AgeRegression 9 +#rm -f ../share/openbr/models/algorithms/AgeRegression
10 10
11 -br -algorithm AgeRegression -path ../data/PCSO/Images -train "../data/PCSO/PCSO.db[query='SELECT File,Age,PersonID FROM PCSO WHERE Age >= 15 AND AGE <= 75', subset=0:200]" ../share/openbr/models/algorithms/AgeRegression 11 +export BR=../build/app/br/br
  12 +export ageAlg=AgeRegression
  13 +
  14 +export PCSO_DIR=/user/pripshare/Databases/FaceDatabases/PCSO/PCSO/
  15 +
  16 +$BR -useGui 0 -algorithm $ageAlg -path $PCSO_DIR/Images -train "$PCSO_DIR/PCSO.db[query='SELECT File,Age,PersonID FROM PCSO WHERE Age >= 17 AND AGE <= 68', subset=0:200]" ../share/openbr/models/algorithms/AgeRegression
scripts/trainFaceRecognition-PCSO.sh
@@ -8,6 +8,13 @@ fi @@ -8,6 +8,13 @@ fi
8 #rm -f ../share/openbr/models/features/FaceRecognitionExtraction 8 #rm -f ../share/openbr/models/features/FaceRecognitionExtraction
9 #rm -f ../share/openbr/models/features/FaceRecognitionEmbedding 9 #rm -f ../share/openbr/models/features/FaceRecognitionEmbedding
10 #rm -f ../share/openbr/models/features/FaceRecognitionQuantization 10 #rm -f ../share/openbr/models/features/FaceRecognitionQuantization
11 -rm -f ../share/openbr/models/algorithms/FaceRecognition 11 +#rm -f ../share/openbr/models/algorithms/FaceRecognition
  12 +
  13 +export BR=../build/app/br/br
  14 +
  15 +export PCSO_DIR=/user/pripshare/Databases/FaceDatabases/PCSO/PCSO/
  16 +
  17 +
  18 +
  19 +$BR -useGui 0 -algorithm FaceRecognition -path "$PCSO_DIR/Images/" -train "$PCSO_DIR/PCSO.db[query='SELECT File,PersonID as Label,PersonID FROM PCSO', subset=0:5:6000]" ../share/openbr/models/algorithms/FaceRecognition
12 20
13 -br -algorithm FaceRecognition -path ../data/PCSO/img -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
@@ -6,6 +6,11 @@ fi @@ -6,6 +6,11 @@ fi
6 6
7 #rm -f ../share/openbr/models/features/FaceClassificationRegistration 7 #rm -f ../share/openbr/models/features/FaceClassificationRegistration
8 #rm -f ../share/openbr/models/features/FaceClassificationExtraction 8 #rm -f ../share/openbr/models/features/FaceClassificationExtraction
9 -rm -f ../share/openbr/models/algorithms/GenderClassification 9 +#rm -f ../share/openbr/models/algorithms/GenderClassification
10 10
11 -br -algorithm GenderClassification -path ../data/PCSO/Images -train "../data/PCSO/PCSO.db[query='SELECT File,Gender,PersonID FROM PCSO', subset=0:8000]" ../share/openbr/models/algorithms/GenderClassification 11 +export BR=../build/app/br/br
  12 +export genderAlg=GenderClassification
  13 +
  14 +export PCSO_DIR=/user/pripshare/Databases/FaceDatabases/PCSO/PCSO/
  15 +
  16 +$BR -useGui 0 -algorithm $genderAlg -path $PCSO_DIR/Images -train "$PCSO_DIR/PCSO.db[query='SELECT File,Gender,PersonID FROM PCSO', subset=0:8000]" ../share/openbr/models/algorithms/GenderClassification
share/openbr/openbr.bib
@@ -34,6 +34,11 @@ @@ -34,6 +34,11 @@
34 Howpublished = {https://github.com/lbestrowden}, 34 Howpublished = {https://github.com/lbestrowden},
35 Title = {bestrow1 at msu.edu}} 35 Title = {bestrow1 at msu.edu}}
36 36
  37 +@misc{imaus10,
  38 + Author = {Austin Van Blanton},
  39 + Howpublished = {https://github.com/imaus10},
  40 + Title = {imaus10 at gmail.com}}
  41 +
37 % Software 42 % Software
38 @misc{libface, 43 @misc{libface,
39 Howpublished = {http://libface.sourceforge.net/file/Home.html}, 44 Howpublished = {http://libface.sourceforge.net/file/Home.html},