Commit 3808a6fe13088d7caf3584a0517f4f9dc4cb5f75

Authored by Josh Klontz
2 parents 4070e564 24aec81e

Merge branch 'master' into attributes

LICENSE.txt
1 -Copyright 2012-2014 The MITRE Corporation 1 +Copyright 2012 The MITRE Corporation
2 2
3 Licensed under the Apache License, Version 2.0 (the "License"); 3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License. 4 you may not use this file except in compliance with the License.
app/br/br.cpp
@@ -138,8 +138,8 @@ public: @@ -138,8 +138,8 @@ public:
138 check(parc >= 2 && parc <= 4, "Incorrect parameter count for 'evalClassification'."); 138 check(parc >= 2 && parc <= 4, "Incorrect parameter count for 'evalClassification'.");
139 br_eval_classification(parv[0], parv[1], parc >= 3 ? parv[2] : "", parc >= 4 ? parv[3] : ""); 139 br_eval_classification(parv[0], parv[1], parc >= 3 ? parv[2] : "", parc >= 4 ? parv[3] : "");
140 } else if (!strcmp(fun, "evalClustering")) { 140 } else if (!strcmp(fun, "evalClustering")) {
141 - check(parc == 2, "Incorrect parameter count for 'evalClustering'.");  
142 - br_eval_clustering(parv[0], parv[1]); 141 + check((parc >= 2) && (parc <= 3), "Incorrect parameter count for 'evalClustering'.");
  142 + br_eval_clustering(parv[0], parv[1], parc == 3 ? parv[2] : "");
143 } else if (!strcmp(fun, "evalDetection")) { 143 } else if (!strcmp(fun, "evalDetection")) {
144 check((parc >= 2) && (parc <= 3), "Incorrect parameter count for 'evalDetection'."); 144 check((parc >= 2) && (parc <= 3), "Incorrect parameter count for 'evalDetection'.");
145 br_eval_detection(parv[0], parv[1], parc == 3 ? parv[2] : ""); 145 br_eval_detection(parv[0], parv[1], parc == 3 ? parv[2] : "");
openbr/core/cluster.cpp
@@ -100,7 +100,9 @@ Neighborhood getNeighborhood(const QStringList &amp;simmats) @@ -100,7 +100,9 @@ Neighborhood getNeighborhood(const QStringList &amp;simmats)
100 int currentRows = -1; 100 int currentRows = -1;
101 int columnOffset = 0; 101 int columnOffset = 0;
102 for (int j=0; j<numGalleries; j++) { 102 for (int j=0; j<numGalleries; j++) {
103 - cv::Mat m = BEE::readMat(simmats[i*numGalleries+j]); 103 + QScopedPointer<br::Format> format(br::Factory<br::Format>::make(simmats[i*numGalleries+j]));
  104 + br::Template t = format->read();
  105 + cv::Mat m = t.m();
104 if (j==0) { 106 if (j==0) {
105 currentRows = m.rows; 107 currentRows = m.rows;
106 allNeighbors.resize(currentRows); 108 allNeighbors.resize(currentRows);
@@ -115,8 +117,9 @@ Neighborhood getNeighborhood(const QStringList &amp;simmats) @@ -115,8 +117,9 @@ Neighborhood getNeighborhood(const QStringList &amp;simmats)
115 float val = m.at<float>(k,l); 117 float val = m.at<float>(k,l);
116 if ((i==j) && (k==l)) continue; // Skips self-similarity scores 118 if ((i==j) && (k==l)) continue; // Skips self-similarity scores
117 119
118 - if ((val != -std::numeric_limits<float>::infinity()) &&  
119 - (val != std::numeric_limits<float>::infinity())) { 120 + if (val != -std::numeric_limits<float>::max()
  121 + && val != -std::numeric_limits<float>::infinity()
  122 + && val != std::numeric_limits<float>::infinity()) {
120 globalMax = std::max(globalMax, val); 123 globalMax = std::max(globalMax, val);
121 globalMin = std::min(globalMin, val); 124 globalMin = std::min(globalMin, val);
122 } 125 }
@@ -157,7 +160,7 @@ Neighborhood getNeighborhood(const QStringList &amp;simmats) @@ -157,7 +160,7 @@ Neighborhood getNeighborhood(const QStringList &amp;simmats)
157 // Zhu et al. "A Rank-Order Distance based Clustering Algorithm for Face Tagging", CVPR 2011 160 // Zhu et al. "A Rank-Order Distance based Clustering Algorithm for Face Tagging", CVPR 2011
158 br::Clusters br::ClusterGallery(const QStringList &simmats, float aggressiveness, const QString &csv) 161 br::Clusters br::ClusterGallery(const QStringList &simmats, float aggressiveness, const QString &csv)
159 { 162 {
160 - qDebug("Clustering %d simmat(s)", simmats.size()); 163 + qDebug("Clustering %d simmat(s), aggressiveness %f", simmats.size(), aggressiveness);
161 164
162 // Read in gallery parts, keeping top neighbors of each template 165 // Read in gallery parts, keeping top neighbors of each template
163 Neighborhood neighborhood = getNeighborhood(simmats); 166 Neighborhood neighborhood = getNeighborhood(simmats);
@@ -275,13 +278,14 @@ float jaccardIndex(const QVector&lt;int&gt; &amp;indicesA, const QVector&lt;int&gt; &amp;indicesB) @@ -275,13 +278,14 @@ float jaccardIndex(const QVector&lt;int&gt; &amp;indicesA, const QVector&lt;int&gt; &amp;indicesB)
275 278
276 // Evaluates clustering algorithms based on metrics described in 279 // Evaluates clustering algorithms based on metrics described in
277 // Santo Fortunato "Community detection in graphs", Physics Reports 486 (2010) 280 // Santo Fortunato "Community detection in graphs", Physics Reports 486 (2010)
278 -void br::EvalClustering(const QString &csv, const QString &input) 281 +void br::EvalClustering(const QString &csv, const QString &input, QString truth_property)
279 { 282 {
  283 + if (truth_property.isEmpty())
  284 + truth_property = "Label";
280 qDebug("Evaluating %s against %s", qPrintable(csv), qPrintable(input)); 285 qDebug("Evaluating %s against %s", qPrintable(csv), qPrintable(input));
281 286
282 - // We assume clustering algorithms store assigned cluster labels as integers (since the clusters are  
283 - // not named). Direct use of ClusterID is not general -cao  
284 - QList<int> labels = File::get<int>(TemplateList::fromGallery(input), "ClusterID"); 287 + TemplateList tList = TemplateList::fromGallery(input);
  288 + QList<int> labels = tList.indexProperty(truth_property);
285 289
286 QHash<int, int> labelToIndex; 290 QHash<int, int> labelToIndex;
287 int nClusters = 0; 291 int nClusters = 0;
openbr/core/cluster.h
@@ -28,7 +28,7 @@ namespace br @@ -28,7 +28,7 @@ namespace br
28 typedef QVector<Cluster> Clusters; 28 typedef QVector<Cluster> Clusters;
29 29
30 Clusters ClusterGallery(const QStringList &simmats, float aggressiveness, const QString &csv); 30 Clusters ClusterGallery(const QStringList &simmats, float aggressiveness, const QString &csv);
31 - void EvalClustering(const QString &csv, const QString &input); 31 + void EvalClustering(const QString &csv, const QString &input, QString truth_property);
32 32
33 Clusters ReadClusters(const QString &csv); 33 Clusters ReadClusters(const QString &csv);
34 void WriteClusters(const Clusters &clusters, const QString &csv); 34 void WriteClusters(const Clusters &clusters, const QString &csv);
openbr/core/common.cpp
@@ -76,3 +76,11 @@ QList&lt;float&gt; Common::linspace(float start, float stop, int n) { @@ -76,3 +76,11 @@ QList&lt;float&gt; Common::linspace(float start, float stop, int n) {
76 spaced.append(stop); 76 spaced.append(stop);
77 return spaced; 77 return spaced;
78 } 78 }
  79 +
  80 +QList<int> Common::ind2sub(int dims, int nPerDim, int idx) {
  81 + QList<int> subIndices;
  82 + for (int j = 0; j < dims; j++) {
  83 + subIndices.append(((int)floor( idx / pow((float)nPerDim, j))) % nPerDim);
  84 + }
  85 + return subIndices;
  86 +}
openbr/core/common.h
@@ -335,6 +335,10 @@ V&lt;T&gt; Downsample(V&lt;T&gt; vals, int k) @@ -335,6 +335,10 @@ V&lt;T&gt; Downsample(V&lt;T&gt; vals, int k)
335 return newVals; 335 return newVals;
336 } 336 }
337 337
  338 +/*! \brief Converts index into subdimensions.
  339 +*/
  340 +QList<int> ind2sub(int dims, int nPerDim, int idx);
  341 +
338 } 342 }
339 343
340 #endif // COMMON_COMMON_H 344 #endif // COMMON_COMMON_H
openbr/core/eigenutils.cpp
@@ -67,3 +67,12 @@ void printEigen(Eigen::MatrixXf X) { @@ -67,3 +67,12 @@ void printEigen(Eigen::MatrixXf X) {
67 void printSize(Eigen::MatrixXf X) { 67 void printSize(Eigen::MatrixXf X) {
68 qDebug() << "Rows=" << X.rows() << "\tCols=" << X.cols(); 68 qDebug() << "Rows=" << X.rows() << "\tCols=" << X.cols();
69 } 69 }
  70 +
  71 +float eigMean(const Eigen::MatrixXf& x) {
  72 + return x.array().sum() / (x.rows() * x.cols());
  73 +}
  74 +
  75 +float eigStd(const Eigen::MatrixXf& x) {
  76 + float mean = eigMean(x);
  77 + return sqrt((x.array() - mean).pow(2).sum() / (x.cols() * x.rows()));
  78 +}
openbr/core/eigenutils.h
@@ -67,4 +67,55 @@ inline QDataStream &amp;operator&gt;&gt;(QDataStream &amp;stream, Eigen::Matrix&lt; _Scalar, _Row @@ -67,4 +67,55 @@ inline QDataStream &amp;operator&gt;&gt;(QDataStream &amp;stream, Eigen::Matrix&lt; _Scalar, _Row
67 return stream; 67 return stream;
68 } 68 }
69 69
  70 +/*Compute the mean of the each column (dim == 1) or row (dim == 2)
  71 + of the matrix*/
  72 +template<typename T>
  73 +Eigen::MatrixBase<T> eigMean(const Eigen::MatrixBase<T>& x,int dim)
  74 +{
  75 + if (dim == 1) {
  76 + Eigen::MatrixBase<T> y(1,x.cols());
  77 + for (int i = 0; i < x.cols(); i++)
  78 + y(i) = x.col(i).sum() / x.rows();
  79 + return y;
  80 + } else if (dim == 2) {
  81 + Eigen::MatrixBase<T> y(x.rows(),1);
  82 + for (int i = 0; i < x.rows(); i++)
  83 + y(i) = x.row(i).sum() / x.cols();
  84 + return y;
  85 + }
  86 + qFatal("A matrix can only have two dimensions");
  87 +}
  88 +
  89 +/*Compute the element-wise mean*/
  90 +float eigMean(const Eigen::MatrixXf& x);
  91 +/*Compute the element-wise mean*/
  92 +float eigStd(const Eigen::MatrixXf& x);
  93 +
  94 +/*Compute the std dev of the each column (dim == 1) or row (dim == 2)
  95 + of the matrix*/
  96 +template<typename T>
  97 +Eigen::MatrixBase<T> eigStd(const Eigen::MatrixBase<T>& x,int dim)
  98 +{
  99 + Eigen::MatrixBase<T> mean = eigMean(x, dim);
  100 + if (dim == 1) {
  101 + Eigen::MatrixBase<T> y(1,x.cols());
  102 + for (int i = 0; i < x.cols(); i++) {
  103 + T value = 0;
  104 + for (int j = 0; j < x.rows(); j++)
  105 + value += pow(y(j, i) - mean(i), 2);
  106 + y(i) = sqrt(value / (x.rows() - 1));
  107 + }
  108 + return y;
  109 + } else if (dim == 2) {
  110 + Eigen::MatrixBase<T> y(x.rows(),1);
  111 + for (int i = 0; i < x.rows(); i++) {
  112 + T value = 0;
  113 + for (int j = 0; j < x.cols(); j++)
  114 + value += pow(y(i, j) - mean(j), 2);
  115 + y(i) = sqrt(value / (x.cols() - 1));
  116 + }
  117 + return y;
  118 + }
  119 + qFatal("A matrix can only have two dimensions");
  120 +}
70 #endif // EIGENUTILS_H 121 #endif // EIGENUTILS_H
openbr/core/eval.cpp
@@ -572,6 +572,7 @@ float EvalLandmarking(const QString &amp;predictedGallery, const QString &amp;truthGalle @@ -572,6 +572,7 @@ float EvalLandmarking(const QString &amp;predictedGallery, const QString &amp;truthGalle
572 const QStringList predictedNames = File::get<QString>(predicted, "name"); 572 const QStringList predictedNames = File::get<QString>(predicted, "name");
573 const QStringList truthNames = File::get<QString>(truth, "name"); 573 const QStringList truthNames = File::get<QString>(truth, "name");
574 574
  575 + int skipped = 0;
575 QList< QList<float> > pointErrors; 576 QList< QList<float> > pointErrors;
576 for (int i=0; i<predicted.size(); i++) { 577 for (int i=0; i<predicted.size(); i++) {
577 const QString &predictedName = predictedNames[i]; 578 const QString &predictedName = predictedNames[i];
@@ -579,7 +580,10 @@ float EvalLandmarking(const QString &amp;predictedGallery, const QString &amp;truthGalle @@ -579,7 +580,10 @@ float EvalLandmarking(const QString &amp;predictedGallery, const QString &amp;truthGalle
579 if (truthIndex == -1) qFatal("Could not identify ground truth for file: %s", qPrintable(predictedName)); 580 if (truthIndex == -1) qFatal("Could not identify ground truth for file: %s", qPrintable(predictedName));
580 const QList<QPointF> predictedPoints = predicted[i].file.points(); 581 const QList<QPointF> predictedPoints = predicted[i].file.points();
581 const QList<QPointF> truthPoints = truth[truthIndex].file.points(); 582 const QList<QPointF> truthPoints = truth[truthIndex].file.points();
582 - if (predictedPoints.size() != truthPoints.size()) qFatal("Points size mismatch for file: %s", qPrintable(predictedName)); 583 + if (predictedPoints.size() != truthPoints.size()) {
  584 + skipped++;
  585 + continue;
  586 + }
583 while (pointErrors.size() < predictedPoints.size()) 587 while (pointErrors.size() < predictedPoints.size())
584 pointErrors.append(QList<float>()); 588 pointErrors.append(QList<float>());
585 if (normalizationIndexA >= truthPoints.size()) qFatal("Normalization index A is out of range."); 589 if (normalizationIndexA >= truthPoints.size()) qFatal("Normalization index A is out of range.");
@@ -588,6 +592,7 @@ float EvalLandmarking(const QString &amp;predictedGallery, const QString &amp;truthGalle @@ -588,6 +592,7 @@ float EvalLandmarking(const QString &amp;predictedGallery, const QString &amp;truthGalle
588 for (int j=0; j<predictedPoints.size(); j++) 592 for (int j=0; j<predictedPoints.size(); j++)
589 pointErrors[j].append(QtUtils::euclideanLength(predictedPoints[j] - truthPoints[j])/normalizedLength); 593 pointErrors[j].append(QtUtils::euclideanLength(predictedPoints[j] - truthPoints[j])/normalizedLength);
590 } 594 }
  595 + qDebug() << "Skipped " << skipped << " files do to point size mismatch.";
591 596
592 QList<float> averagePointErrors; averagePointErrors.reserve(pointErrors.size()); 597 QList<float> averagePointErrors; averagePointErrors.reserve(pointErrors.size());
593 for (int i=0; i<pointErrors.size(); i++) { 598 for (int i=0; i<pointErrors.size(); i++) {
@@ -605,6 +610,8 @@ float EvalLandmarking(const QString &amp;predictedGallery, const QString &amp;truthGalle @@ -605,6 +610,8 @@ float EvalLandmarking(const QString &amp;predictedGallery, const QString &amp;truthGalle
605 lines.append(QString("Box,%1,%2").arg(QString::number(i), QString::number(pointError[j*(pointError.size()-1)/(keep-1)]))); 610 lines.append(QString("Box,%1,%2").arg(QString::number(i), QString::number(pointError[j*(pointError.size()-1)/(keep-1)])));
606 } 611 }
607 612
  613 + lines.append(QString("AvgError,0,%1").arg(averagePointError));
  614 +
608 QtUtils::writeFile(csv, lines); 615 QtUtils::writeFile(csv, lines);
609 qDebug("Average Error: %.3f", averagePointError); 616 qDebug("Average Error: %.3f", averagePointError);
610 return averagePointError; 617 return averagePointError;
openbr/core/qtutils.cpp
@@ -24,6 +24,7 @@ @@ -24,6 +24,7 @@
24 #include <QProcess> 24 #include <QProcess>
25 #include <QProcessEnvironment> 25 #include <QProcessEnvironment>
26 #include <QRegExp> 26 #include <QRegExp>
  27 +#include <QRegularExpression>
27 #include <QStack> 28 #include <QStack>
28 #include <QUrl> 29 #include <QUrl>
29 #include <QMap> 30 #include <QMap>
@@ -81,7 +82,7 @@ void readFile(const QString &amp;file, QStringList &amp;lines) @@ -81,7 +82,7 @@ void readFile(const QString &amp;file, QStringList &amp;lines)
81 { 82 {
82 QByteArray data; 83 QByteArray data;
83 readFile(file, data); 84 readFile(file, data);
84 - lines = QString(data).split('\n', QString::SkipEmptyParts); 85 + lines = QString(data).split(QRegularExpression("[\n|\r\n|\r]"), QString::SkipEmptyParts);
85 for (int i=0; i<lines.size(); i++) 86 for (int i=0; i<lines.size(); i++)
86 lines[i] = lines[i].simplified(); 87 lines[i] = lines[i].simplified();
87 } 88 }
openbr/openbr.cpp
@@ -109,9 +109,9 @@ void br_eval_classification(const char *predicted_gallery, const char *truth_gal @@ -109,9 +109,9 @@ void br_eval_classification(const char *predicted_gallery, const char *truth_gal
109 EvalClassification(predicted_gallery, truth_gallery, predicted_property, truth_property); 109 EvalClassification(predicted_gallery, truth_gallery, predicted_property, truth_property);
110 } 110 }
111 111
112 -void br_eval_clustering(const char *csv, const char *gallery) 112 +void br_eval_clustering(const char *csv, const char *gallery, const char * truth_property)
113 { 113 {
114 - EvalClustering(csv, gallery); 114 + EvalClustering(csv, gallery, truth_property);
115 } 115 }
116 116
117 float br_eval_detection(const char *predicted_gallery, const char *truth_gallery, const char *csv) 117 float br_eval_detection(const char *predicted_gallery, const char *truth_gallery, const char *csv)
@@ -435,7 +435,14 @@ br_template_list br_load_from_gallery(br_gallery gallery) @@ -435,7 +435,14 @@ br_template_list br_load_from_gallery(br_gallery gallery)
435 return (br_template_list)tl; 435 return (br_template_list)tl;
436 } 436 }
437 437
438 -void br_add_to_gallery(br_gallery gallery, br_template_list tl) 438 +void br_add_template_to_gallery(br_gallery gallery, br_template tmpl)
  439 +{
  440 + Gallery *gal = reinterpret_cast<Gallery*>(gallery);
  441 + Template *t = reinterpret_cast<Template*>(tmpl);
  442 + gal->write(*t);
  443 +}
  444 +
  445 +void br_add_template_list_to_gallery(br_gallery gallery, br_template_list tl)
439 { 446 {
440 Gallery *gal = reinterpret_cast<Gallery*>(gallery); 447 Gallery *gal = reinterpret_cast<Gallery*>(gallery);
441 TemplateList *realTL = reinterpret_cast<TemplateList*>(tl); 448 TemplateList *realTL = reinterpret_cast<TemplateList*>(tl);
openbr/openbr.h
@@ -167,9 +167,10 @@ BR_EXPORT void br_eval_classification(const char *predicted_gallery, const char @@ -167,9 +167,10 @@ BR_EXPORT void br_eval_classification(const char *predicted_gallery, const char
167 * \brief Evaluates and prints clustering accuracy to the terminal. 167 * \brief Evaluates and prints clustering accuracy to the terminal.
168 * \param csv The cluster results file. 168 * \param csv The cluster results file.
169 * \param gallery The br::Gallery used to generate the \ref simmat that was clustered. 169 * \param gallery The br::Gallery used to generate the \ref simmat that was clustered.
  170 + * \param truth_property (Optional) which metadata key to use from <i>gallery</i/>, defaults to Label
170 * \see br_cluster 171 * \see br_cluster
171 */ 172 */
172 -BR_EXPORT void br_eval_clustering(const char *csv, const char *gallery); 173 +BR_EXPORT void br_eval_clustering(const char *csv, const char *gallery, const char * truth_property);
173 174
174 /*! 175 /*!
175 * \brief Evaluates and prints detection accuracy to terminal. 176 * \brief Evaluates and prints detection accuracy to terminal.
@@ -562,9 +563,13 @@ BR_EXPORT br_gallery br_make_gallery(const char *gallery); @@ -562,9 +563,13 @@ BR_EXPORT br_gallery br_make_gallery(const char *gallery);
562 */ 563 */
563 BR_EXPORT br_template_list br_load_from_gallery(br_gallery gallery); 564 BR_EXPORT br_template_list br_load_from_gallery(br_gallery gallery);
564 /*! 565 /*!
  566 + * \brief Write a br::Template to the br::Gallery on disk.
  567 + */
  568 +BR_EXPORT void br_add_template_to_gallery(br_gallery gallery, br_template tmpl);
  569 +/*!
565 * \brief Write a br::TemplateList to the br::Gallery on disk. 570 * \brief Write a br::TemplateList to the br::Gallery on disk.
566 */ 571 */
567 -BR_EXPORT void br_add_to_gallery(br_gallery gallery, br_template_list tl); 572 +BR_EXPORT void br_add_template_list_to_gallery(br_gallery gallery, br_template_list tl);
568 /*! 573 /*!
569 * \brief Close the br::Gallery. 574 * \brief Close the br::Gallery.
570 */ 575 */
openbr/openbr_plugin.cpp
@@ -137,6 +137,8 @@ QVariant File::parse(const QString &amp;value) @@ -137,6 +137,8 @@ QVariant File::parse(const QString &amp;value)
137 if (ok) return point; 137 if (ok) return point;
138 const QRectF rect = QtUtils::toRect(value, &ok); 138 const QRectF rect = QtUtils::toRect(value, &ok);
139 if (ok) return rect; 139 if (ok) return rect;
  140 + const int i = value.toInt(&ok);
  141 + if (ok) return i;
140 const float f = value.toFloat(&ok); 142 const float f = value.toFloat(&ok);
141 if (ok) return f; 143 if (ok) return f;
142 return value; 144 return value;
openbr/plugins/algorithms.cpp
@@ -44,7 +44,7 @@ class AlgorithmsInitializer : public Initializer @@ -44,7 +44,7 @@ class AlgorithmsInitializer : public Initializer
44 Globals->abbreviations.insert("AgeEstimation", "AgeRegression"); 44 Globals->abbreviations.insert("AgeEstimation", "AgeRegression");
45 Globals->abbreviations.insert("FaceRecognition2", "{PP5Register+Affine(128,128,0.25,0.35)+Cvt(Gray)}+(Gradient+Bin(0,360,9,true))/(Blur(1)+Gamma(0.2)+DoG(1,2)+ContrastEq(0.1,10)+LBP(1,2,true)+Bin(0,10,10,true))+Merge+Integral+RecursiveIntegralSampler(4,2,8,LDA(.98)+Normalize(L1))+Cat+PCA(768)+Normalize(L1)+Quantize:UCharL1"); 45 Globals->abbreviations.insert("FaceRecognition2", "{PP5Register+Affine(128,128,0.25,0.35)+Cvt(Gray)}+(Gradient+Bin(0,360,9,true))/(Blur(1)+Gamma(0.2)+DoG(1,2)+ContrastEq(0.1,10)+LBP(1,2,true)+Bin(0,10,10,true))+Merge+Integral+RecursiveIntegralSampler(4,2,8,LDA(.98)+Normalize(L1))+Cat+PCA(768)+Normalize(L1)+Quantize:UCharL1");
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 - Globals->abbreviations.insert("4SF", "Open+Cvt(Gray)+Cascade(FrontalFace)+ASEFEyes+Affine(128,128,0.33,0.45)+(Grid(10,10)+SIFTDescriptor(12)+ByRow)/(Blur(1.1)+Gamma(0.2)+DoG(1,2)+ContrastEq(0.1,10)+LBP(1,2)+RectRegions(8,8,6,6)+Hist(59))+PCA(0.95)+Normalize(L2)+Dup(12)+RndSubspace(0.05,1)+LDA(0.98)+Cat+PCA(0.95)+Normalize(L1)+Quantize:NegativeLogPlusOne(ByteL1)"); 47 + Globals->abbreviations.insert("4SF", "Open+Cvt(Gray)+Cascade(FrontalFace)+ASEFEyes+Affine(128,128,0.33,0.45)+(Grid(10,10)+SIFTDescriptor(12)+ByRow)/(Blur(1.1)+Gamma(0.2)+DoG(1,2)+ContrastEq(0.1,10)+LBP(1,2)+RectRegions(8,8,6,6)+Hist(59))+PCA(0.95)+Cat+Normalize(L2)+Dup(12)+RndSubspace(0.05,1)+LDA(0.98)+Cat+PCA(0.95)+Normalize(L1)+Quantize:NegativeLogPlusOne(ByteL1)");
48 48
49 // Video 49 // Video
50 Globals->abbreviations.insert("DisplayVideo", "Stream(FPSLimit(30)+Show(false,[FrameNumber])+Discard)"); 50 Globals->abbreviations.insert("DisplayVideo", "Stream(FPSLimit(30)+Show(false,[FrameNumber])+Discard)");
@@ -64,6 +64,7 @@ class AlgorithmsInitializer : public Initializer @@ -64,6 +64,7 @@ class AlgorithmsInitializer : public Initializer
64 Globals->abbreviations.insert("SmallSIFT", "Open+LimitSize(512)+KeyPointDetector(SIFT)+KeyPointDescriptor(SIFT):KeyPointMatcher(BruteForce)"); 64 Globals->abbreviations.insert("SmallSIFT", "Open+LimitSize(512)+KeyPointDetector(SIFT)+KeyPointDescriptor(SIFT):KeyPointMatcher(BruteForce)");
65 Globals->abbreviations.insert("SmallSURF", "Open+LimitSize(512)+KeyPointDetector(SURF)+KeyPointDescriptor(SURF):KeyPointMatcher(BruteForce)"); 65 Globals->abbreviations.insert("SmallSURF", "Open+LimitSize(512)+KeyPointDetector(SURF)+KeyPointDescriptor(SURF):KeyPointMatcher(BruteForce)");
66 Globals->abbreviations.insert("ColorHist", "Open+LimitSize(512)+Expand+EnsureChannels(3)+SplitChannels+Hist(256,0,8)+Cat+Normalize(L1):L2"); 66 Globals->abbreviations.insert("ColorHist", "Open+LimitSize(512)+Expand+EnsureChannels(3)+SplitChannels+Hist(256,0,8)+Cat+Normalize(L1):L2");
  67 + Globals->abbreviations.insert("ImageSimilarity", "Open+EnsureChannels(3)+Resize(256,256)+SplitChannels+RectRegions(64,64,64,64)+Hist(256,0,8)+Cat:NegativeLogPlusOne(L2)");
67 Globals->abbreviations.insert("ImageClassification", "Open+CropSquare+LimitSize(256)+Cvt(Gray)+Gradient+Bin(0,360,9,true)+Merge+Integral+RecursiveIntegralSampler(4,2,8,Singleton(KMeans(256)))+Cat+CvtFloat+Hist(256)+KNN(5,Dist(L1),false,5)+Rename(KNN,Subject)"); 68 Globals->abbreviations.insert("ImageClassification", "Open+CropSquare+LimitSize(256)+Cvt(Gray)+Gradient+Bin(0,360,9,true)+Merge+Integral+RecursiveIntegralSampler(4,2,8,Singleton(KMeans(256)))+Cat+CvtFloat+Hist(256)+KNN(5,Dist(L1),false,5)+Rename(KNN,Subject)");
68 Globals->abbreviations.insert("TanTriggs", "Blur(1.1)+Gamma(0.2)+DoG(1,2)+ContrastEq(0.1,10)"); 69 Globals->abbreviations.insert("TanTriggs", "Blur(1.1)+Gamma(0.2)+DoG(1,2)+ContrastEq(0.1,10)");
69 70
openbr/plugins/eigen3.cpp
@@ -15,15 +15,35 @@ @@ -15,15 +15,35 @@
15 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 15 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16 16
17 #include <Eigen/Dense> 17 #include <Eigen/Dense>
  18 +
18 #include "openbr_internal.h" 19 #include "openbr_internal.h"
19 20
20 #include "openbr/core/common.h" 21 #include "openbr/core/common.h"
21 #include "openbr/core/eigenutils.h" 22 #include "openbr/core/eigenutils.h"
  23 +#include "openbr/core/opencvutils.h"
22 24
23 namespace br 25 namespace br
24 { 26 {
25 27
26 /*! 28 /*!
  29 + * \ingroup initializers
  30 + * \brief Initialize Eigen
  31 + * http://eigen.tuxfamily.org/dox/TopicMultiThreading.html
  32 + * \author Scott Klum \cite sklum
  33 + */
  34 +class EigenInitializer : public Initializer
  35 +{
  36 + Q_OBJECT
  37 +
  38 + void initialize() const
  39 + {
  40 + Eigen::initParallel();
  41 + }
  42 +};
  43 +
  44 +BR_REGISTER(Initializer, EigenInitializer)
  45 +
  46 +/*!
27 * \ingroup transforms 47 * \ingroup transforms
28 * \brief Projects input into learned Principal Component Analysis subspace. 48 * \brief Projects input into learned Principal Component Analysis subspace.
29 * \author Brendan Klare \cite bklare 49 * \author Brendan Klare \cite bklare
@@ -296,6 +316,8 @@ BR_REGISTER(Transform, DFFSTransform) @@ -296,6 +316,8 @@ BR_REGISTER(Transform, DFFSTransform)
296 */ 316 */
297 class LDATransform : public Transform 317 class LDATransform : public Transform
298 { 318 {
  319 + friend class SparseLDATransform;
  320 +
299 Q_OBJECT 321 Q_OBJECT
300 Q_PROPERTY(float pcaKeep READ get_pcaKeep WRITE set_pcaKeep RESET reset_pcaKeep STORED false) 322 Q_PROPERTY(float pcaKeep READ get_pcaKeep WRITE set_pcaKeep RESET reset_pcaKeep STORED false)
301 Q_PROPERTY(bool pcaWhiten READ get_pcaWhiten WRITE set_pcaWhiten RESET reset_pcaWhiten STORED false) 323 Q_PROPERTY(bool pcaWhiten READ get_pcaWhiten WRITE set_pcaWhiten RESET reset_pcaWhiten STORED false)
@@ -499,21 +521,30 @@ class LDATransform : public Transform @@ -499,21 +521,30 @@ class LDATransform : public Transform
499 521
500 // Do projection 522 // Do projection
501 outMap = projection.transpose() * (inMap - mean); 523 outMap = projection.transpose() * (inMap - mean);
502 -  
503 if (normalize && isBinary) 524 if (normalize && isBinary)
504 dst.m().at<float>(0,0) = dst.m().at<float>(0,0) / stdDev; 525 dst.m().at<float>(0,0) = dst.m().at<float>(0,0) / stdDev;
505 } 526 }
506 527
507 void store(QDataStream &stream) const 528 void store(QDataStream &stream) const
508 { 529 {
509 - stream << pcaKeep << directLDA << directDrop << dimsOut << mean << projection; 530 + stream << pcaKeep;
  531 + stream << directLDA;
  532 + stream << directDrop;
  533 + stream << dimsOut;
  534 + stream << mean;
  535 + stream << projection;
510 if (normalize && isBinary) 536 if (normalize && isBinary)
511 stream << stdDev; 537 stream << stdDev;
512 } 538 }
513 539
514 void load(QDataStream &stream) 540 void load(QDataStream &stream)
515 { 541 {
516 - stream >> pcaKeep >> directLDA >> directDrop >> dimsOut >> mean >> projection; 542 + stream >> pcaKeep;
  543 + stream >> directLDA;
  544 + stream >> directDrop;
  545 + stream >> dimsOut;
  546 + stream >> mean;
  547 + stream >> projection;
517 if (normalize && isBinary) 548 if (normalize && isBinary)
518 stream >> stdDev; 549 stream >> stdDev;
519 } 550 }
@@ -522,6 +553,104 @@ class LDATransform : public Transform @@ -522,6 +553,104 @@ class LDATransform : public Transform
522 BR_REGISTER(Transform, LDATransform) 553 BR_REGISTER(Transform, LDATransform)
523 554
524 /*! 555 /*!
  556 + * \ingroup transforms
  557 + * \brief Projects input into learned Linear Discriminant Analysis subspace
  558 + * learned on a sparse subset of features with the highest weight
  559 + * in the original LDA algorithm.
  560 + * \author Brendan Klare \cite bklare
  561 + */
  562 +class SparseLDATransform : public Transform
  563 +{
  564 + Q_OBJECT
  565 + Q_PROPERTY(float varThreshold READ get_varThreshold WRITE set_varThreshold RESET reset_varThreshold STORED false)
  566 + Q_PROPERTY(float pcaKeep READ get_pcaKeep WRITE set_pcaKeep RESET reset_pcaKeep STORED false)
  567 + Q_PROPERTY(bool normalize READ get_normalize WRITE set_normalize RESET reset_normalize STORED false)
  568 + BR_PROPERTY(float, varThreshold, 1.5)
  569 + BR_PROPERTY(float, pcaKeep, 0.98)
  570 + BR_PROPERTY(bool, normalize, true)
  571 +
  572 + LDATransform ldaSparse;
  573 + int dimsOut;
  574 + QList<int> selections;
  575 +
  576 + Eigen::VectorXf mean;
  577 +
  578 + void init()
  579 + {
  580 + ldaSparse.init();
  581 + ldaSparse.pcaKeep = pcaKeep;
  582 + ldaSparse.inputVariable = "Label";
  583 + ldaSparse.isBinary = true;
  584 + ldaSparse.normalize = true;
  585 + }
  586 +
  587 + void train(const TemplateList &_trainingSet)
  588 + {
  589 +
  590 + LDATransform ldaOrig;
  591 + ldaOrig.init();
  592 + ldaOrig.inputVariable = "Label";
  593 + ldaOrig.pcaKeep = pcaKeep;
  594 + ldaOrig.isBinary = true;
  595 + ldaOrig.normalize = true;
  596 +
  597 + ldaOrig.train(_trainingSet);
  598 +
  599 + //Only works on binary class problems for now
  600 + assert(ldaOrig.projection.cols() == 1);
  601 + float ldaStd = eigStd(ldaOrig.projection);
  602 + for (int i = 0; i < ldaOrig.projection.rows(); i++)
  603 + if (abs(ldaOrig.projection(i)) > varThreshold * ldaStd)
  604 + selections.append(i);
  605 +
  606 + TemplateList newSet;
  607 + for (int i = 0; i < _trainingSet.size(); i++) {
  608 + cv::Mat x(_trainingSet[i]);
  609 + cv::Mat y = cv::Mat(selections.size(), 1, CV_32FC1);
  610 + int idx = 0;
  611 + int cnt = 0;
  612 + for (int j = 0; j < x.rows; j++)
  613 + for (int k = 0; k < x.cols; k++, cnt++)
  614 + if (selections.contains(cnt))
  615 + y.at<float>(idx++,0) = x.at<float>(j, k);
  616 + newSet.append(Template(_trainingSet[i].file, y));
  617 + }
  618 + ldaSparse.train(newSet);
  619 + dimsOut = ldaSparse.dimsOut;
  620 + }
  621 +
  622 + void project(const Template &src, Template &dst) const
  623 + {
  624 + Eigen::Map<Eigen::MatrixXf> inMap((float*)src.m().ptr<float>(), src.m().rows*src.m().cols, 1);
  625 + Eigen::Map<Eigen::MatrixXf> outMap(dst.m().ptr<float>(), dimsOut, 1);
  626 +
  627 + int d = selections.size();
  628 + cv::Mat inSelect(d,1,CV_32F);
  629 + for (int i = 0; i < d; i++)
  630 + inSelect.at<float>(i) = src.m().at<float>(selections[i]);
  631 + ldaSparse.project(Template(src.file, inSelect), dst);
  632 + }
  633 +
  634 + void store(QDataStream &stream) const
  635 + {
  636 + stream << pcaKeep;
  637 + stream << ldaSparse;
  638 + stream << dimsOut;
  639 + stream << selections;
  640 + }
  641 +
  642 + void load(QDataStream &stream)
  643 + {
  644 + stream >> pcaKeep;
  645 + stream >> ldaSparse;
  646 + stream >> dimsOut;
  647 + stream >> selections;
  648 + }
  649 +};
  650 +
  651 +BR_REGISTER(Transform, SparseLDATransform)
  652 +
  653 +/*!
525 * \ingroup distances 654 * \ingroup distances
526 * \brief L1 distance computed using eigen. 655 * \brief L1 distance computed using eigen.
527 * \author Josh Klontz \cite jklontz 656 * \author Josh Klontz \cite jklontz
openbr/plugins/format.cpp
@@ -151,7 +151,7 @@ class csvFormat : public Format @@ -151,7 +151,7 @@ class csvFormat : public Format
151 { 151 {
152 QFile f(file.name); 152 QFile f(file.name);
153 f.open(QFile::ReadOnly); 153 f.open(QFile::ReadOnly);
154 - QStringList lines(QString(f.readAll()).split('\n')); 154 + QStringList lines(QString(f.readAll()).split(QRegularExpression("[\n|\r\n|\r]"), QString::SkipEmptyParts));
155 f.close(); 155 f.close();
156 156
157 bool isUChar = true; 157 bool isUChar = true;
openbr/plugins/gallery.cpp
@@ -506,29 +506,78 @@ BR_REGISTER(Gallery, csvGallery) @@ -506,29 +506,78 @@ BR_REGISTER(Gallery, csvGallery)
506 * \brief Treats each line as a file. 506 * \brief Treats each line as a file.
507 * \author Josh Klontz \cite jklontz 507 * \author Josh Klontz \cite jklontz
508 * 508 *
509 - * The entire line is treated as the file path. 509 + * The entire line is treated as the file path. An optional label may be specified using a space ' ' separator:
510 * 510 *
  511 +\verbatim
  512 +<FILE>
  513 +<FILE>
  514 +...
  515 +<FILE>
  516 +\endverbatim
  517 + * or
  518 +\verbatim
  519 +<FILE> <LABEL>
  520 +<FILE> <LABEL>
  521 +...
  522 +<FILE> <LABEL>
  523 +\endverbatim
511 * \see csvGallery 524 * \see csvGallery
512 */ 525 */
513 class txtGallery : public Gallery 526 class txtGallery : public Gallery
514 { 527 {
515 Q_OBJECT 528 Q_OBJECT
516 - Q_PROPERTY(QString metadataKey READ get_metadataKey WRITE set_metadataKey RESET reset_metadataKey STORED false)  
517 - BR_PROPERTY(QString, metadataKey, "") 529 + Q_PROPERTY(QString label READ get_label WRITE set_label RESET reset_label STORED false)
  530 + BR_PROPERTY(QString, label, "")
518 531
519 QStringList lines; 532 QStringList lines;
520 533
521 ~txtGallery() 534 ~txtGallery()
522 { 535 {
523 - if (!lines.isEmpty()) QtUtils::writeFile(file.name, lines); 536 + if (!lines.isEmpty())
  537 + QtUtils::writeFile(file.name, lines);
524 } 538 }
525 539
526 TemplateList readBlock(bool *done) 540 TemplateList readBlock(bool *done)
527 { 541 {
528 - *done = true;  
529 TemplateList templates; 542 TemplateList templates;
530 - if (!file.exists()) return templates; 543 + foreach (const QString &line, QtUtils::readLines(file)) {
  544 + int splitIndex = line.lastIndexOf(' ');
  545 + if (splitIndex == -1) templates.append(File(line));
  546 + else templates.append(File(line.mid(0, splitIndex), line.mid(splitIndex+1)));
  547 + }
  548 + *done = true;
  549 + return templates;
  550 + }
  551 +
  552 + void write(const Template &t)
  553 + {
  554 + QString line = t.file.name;
  555 + if (!label.isEmpty())
  556 + line += " " + t.file.get<QString>(label);
  557 + lines.append(line);
  558 + }
  559 +};
531 560
  561 +BR_REGISTER(Gallery, txtGallery)
  562 +/*!
  563 + * \ingroup galleries
  564 + * \brief Treats each line as a call to File::flat()
  565 + * \author Josh Klontz \cite jklontz
  566 + */
  567 +class flatGallery : public Gallery
  568 +{
  569 + Q_OBJECT
  570 + QStringList lines;
  571 +
  572 + ~flatGallery()
  573 + {
  574 + if (!lines.isEmpty())
  575 + QtUtils::writeFile(file.name, lines);
  576 + }
  577 +
  578 + TemplateList readBlock(bool *done)
  579 + {
  580 + TemplateList templates;
532 foreach (const QString &line, QtUtils::readLines(file)) 581 foreach (const QString &line, QtUtils::readLines(file))
533 templates.append(File(line)); 582 templates.append(File(line));
534 *done = true; 583 *done = true;
@@ -537,11 +586,11 @@ class txtGallery : public Gallery @@ -537,11 +586,11 @@ class txtGallery : public Gallery
537 586
538 void write(const Template &t) 587 void write(const Template &t)
539 { 588 {
540 - lines.append(metadataKey.isEmpty() ? t.file.flat() : t.file.get<QString>(metadataKey)); 589 + lines.append(t.file.flat());
541 } 590 }
542 }; 591 };
543 592
544 -BR_REGISTER(Gallery, txtGallery) 593 +BR_REGISTER(Gallery, flatGallery)
545 594
546 /*! 595 /*!
547 * \ingroup galleries 596 * \ingroup galleries
@@ -687,7 +736,9 @@ class dbGallery : public Gallery @@ -687,7 +736,9 @@ class dbGallery : public Gallery
687 if (!subset.isEmpty()) { 736 if (!subset.isEmpty()) {
688 const QStringList &words = subset.split(":"); 737 const QStringList &words = subset.split(":");
689 QtUtils::checkArgsSize("Input", words, 2, 4); 738 QtUtils::checkArgsSize("Input", words, 2, 4);
690 - seed = QtUtils::toInt(words[0]); 739 + if (words[0] == "train") seed = 0;
  740 + else if (words[0] == "test" ) seed = 1;
  741 + else seed = QtUtils::toInt(words[0]);
691 if (words[1].startsWith('{') && words[1].endsWith('}')) { 742 if (words[1].startsWith('{') && words[1].endsWith('}')) {
692 foreach (const QString &regexp, words[1].mid(1, words[1].size()-2).split(",")) 743 foreach (const QString &regexp, words[1].mid(1, words[1].size()-2).split(","))
693 metadataFields.append(QRegExp(regexp)); 744 metadataFields.append(QRegExp(regexp));
@@ -813,16 +864,10 @@ class googleGallery : public Gallery @@ -813,16 +864,10 @@ class googleGallery : public Gallery
813 return templates; 864 return templates;
814 } 865 }
815 866
816 - void write(const Template &t) 867 + void write(const Template &)
817 { 868 {
818 - (void) t;  
819 qFatal("Not supported."); 869 qFatal("Not supported.");
820 } 870 }
821 -  
822 - void init()  
823 - {  
824 - //  
825 - }  
826 }; 871 };
827 872
828 BR_REGISTER(Gallery, googleGallery) 873 BR_REGISTER(Gallery, googleGallery)
openbr/plugins/gui.cpp
@@ -413,13 +413,13 @@ private: @@ -413,13 +413,13 @@ private:
413 413
414 }; 414 };
415 415
416 -class DisplayGUI : public QMainWindow 416 +class GUIWindow : public QMainWindow
417 { 417 {
418 Q_OBJECT 418 Q_OBJECT
419 419
420 public: 420 public:
421 421
422 - DisplayGUI(QWidget * parent = NULL) : QMainWindow(parent) 422 + GUIWindow(QWidget * parent = NULL) : QMainWindow(parent)
423 { 423 {
424 centralWidget = new QWidget(); 424 centralWidget = new QWidget();
425 layout = new QHBoxLayout(); 425 layout = new QHBoxLayout();
@@ -592,8 +592,10 @@ public: @@ -592,8 +592,10 @@ public:
592 template<typename WindowType> 592 template<typename WindowType>
593 void initActual() 593 void initActual()
594 { 594 {
595 - if (!Globals->useGui) 595 + if (!Globals->useGui) {
  596 + qWarning("GUI transform %s created without enabling GUI support.\nRun \"br -gui ...\" to enable GUI support from the command line, or set\nGlobals->useGui to true.", this->metaObject()->className());
596 return; 597 return;
  598 + }
597 599
598 if (displayBuffer) 600 if (displayBuffer)
599 delete displayBuffer; 601 delete displayBuffer;
@@ -795,7 +797,7 @@ class ElicitTransform : public ShowTransform @@ -795,7 +797,7 @@ class ElicitTransform : public ShowTransform
795 797
796 Q_OBJECT 798 Q_OBJECT
797 799
798 - DisplayGUI *gui; 800 + GUIWindow *gui;
799 801
800 public: 802 public:
801 ElicitTransform() : ShowTransform() 803 ElicitTransform() : ShowTransform()
@@ -835,14 +837,16 @@ public: @@ -835,14 +837,16 @@ public:
835 837
836 void init() 838 void init()
837 { 839 {
838 - initActual<DisplayGUI>(); 840 + initActual<GUIWindow>();
839 } 841 }
840 842
841 template<typename GUIType> 843 template<typename GUIType>
842 void initActual() 844 void initActual()
843 { 845 {
844 - if (!Globals->useGui) 846 + if (!Globals->useGui) {
  847 + qWarning("GUI transform %s created without enabling GUI support.\nRun \"br -gui ...\" to enable GUI support from the command line, or set\nGlobals->useGui to true.", this->metaObject()->className());
845 return; 848 return;
  849 + }
846 850
847 TimeVaryingTransform::init(); 851 TimeVaryingTransform::init();
848 852
openbr/plugins/independent.cpp
@@ -9,13 +9,14 @@ using namespace cv; @@ -9,13 +9,14 @@ using namespace cv;
9 namespace br 9 namespace br
10 { 10 {
11 11
12 -static TemplateList Downsample(const TemplateList &templates, int classes, int instances, float fraction, const QString & inputVariable, const QStringList &gallery) 12 +static TemplateList Downsample(const TemplateList &templates, int classes, int instances, float fraction, const QString & inputVariable, const QStringList &gallery, const QStringList &subjects)
13 { 13 {
14 // Return early when no downsampling is required 14 // Return early when no downsampling is required
15 if ((classes == std::numeric_limits<int>::max()) && 15 if ((classes == std::numeric_limits<int>::max()) &&
16 (instances == std::numeric_limits<int>::max()) && 16 (instances == std::numeric_limits<int>::max()) &&
17 (fraction >= 1) && 17 (fraction >= 1) &&
18 - (gallery.isEmpty())) 18 + (gallery.isEmpty()) &&
  19 + (subjects.isEmpty()))
19 return templates; 20 return templates;
20 21
21 const bool atLeast = instances < 0; 22 const bool atLeast = instances < 0;
@@ -67,6 +68,11 @@ static TemplateList Downsample(const TemplateList &amp;templates, int classes, int i @@ -67,6 +68,11 @@ static TemplateList Downsample(const TemplateList &amp;templates, int classes, int i
67 if (!gallery.contains(downsample[i].file.get<QString>("Gallery"))) 68 if (!gallery.contains(downsample[i].file.get<QString>("Gallery")))
68 downsample.removeAt(i); 69 downsample.removeAt(i);
69 70
  71 + if (!subjects.isEmpty())
  72 + for (int i=downsample.size()-1; i>=0; i--)
  73 + if (subjects.contains(downsample[i].file.get<QString>(inputVariable)))
  74 + downsample.removeAt(i);
  75 +
70 return downsample; 76 return downsample;
71 } 77 }
72 78
@@ -79,12 +85,15 @@ class DownsampleTrainingTransform : public Transform @@ -79,12 +85,15 @@ class DownsampleTrainingTransform : public Transform
79 Q_PROPERTY(float fraction READ get_fraction WRITE set_fraction RESET reset_fraction STORED false) 85 Q_PROPERTY(float fraction READ get_fraction WRITE set_fraction RESET reset_fraction STORED false)
80 Q_PROPERTY(QString inputVariable READ get_inputVariable WRITE set_inputVariable RESET reset_inputVariable STORED false) 86 Q_PROPERTY(QString inputVariable READ get_inputVariable WRITE set_inputVariable RESET reset_inputVariable STORED false)
81 Q_PROPERTY(QStringList gallery READ get_gallery WRITE set_gallery RESET reset_gallery STORED false) 87 Q_PROPERTY(QStringList gallery READ get_gallery WRITE set_gallery RESET reset_gallery STORED false)
  88 + Q_PROPERTY(QStringList subjects READ get_subjects WRITE set_subjects RESET reset_subjects STORED false)
82 BR_PROPERTY(br::Transform*, transform, NULL) 89 BR_PROPERTY(br::Transform*, transform, NULL)
83 BR_PROPERTY(int, classes, std::numeric_limits<int>::max()) 90 BR_PROPERTY(int, classes, std::numeric_limits<int>::max())
84 BR_PROPERTY(int, instances, std::numeric_limits<int>::max()) 91 BR_PROPERTY(int, instances, std::numeric_limits<int>::max())
85 BR_PROPERTY(float, fraction, 1) 92 BR_PROPERTY(float, fraction, 1)
86 BR_PROPERTY(QString, inputVariable, "Label") 93 BR_PROPERTY(QString, inputVariable, "Label")
87 BR_PROPERTY(QStringList, gallery, QStringList()) 94 BR_PROPERTY(QStringList, gallery, QStringList())
  95 + BR_PROPERTY(QStringList, subjects, QStringList())
  96 +
88 97
89 void project(const Template & src, Template & dst) const 98 void project(const Template & src, Template & dst) const
90 { 99 {
@@ -97,7 +106,7 @@ class DownsampleTrainingTransform : public Transform @@ -97,7 +106,7 @@ class DownsampleTrainingTransform : public Transform
97 if (!transform || !transform->trainable) 106 if (!transform || !transform->trainable)
98 return; 107 return;
99 108
100 - TemplateList downsampled = Downsample(data, classes, instances, fraction, inputVariable, gallery); 109 + TemplateList downsampled = Downsample(data, classes, instances, fraction, inputVariable, gallery, subjects);
101 110
102 transform->train(downsampled); 111 transform->train(downsampled);
103 } 112 }
openbr/plugins/stream.cpp
@@ -1285,11 +1285,18 @@ public: @@ -1285,11 +1285,18 @@ public:
1285 qWarning("Attempted to train untrainable transform, nothing will happen."); 1285 qWarning("Attempted to train untrainable transform, nothing will happen.");
1286 return; 1286 return;
1287 } 1287 }
  1288 + QList<TemplateList> separated;
  1289 + foreach (const TemplateList & list, data) {
  1290 + foreach(const Template & t, list) {
  1291 + separated.append(TemplateList());
  1292 + separated.last().append(t);
  1293 + }
  1294 + }
1288 1295
1289 for (int i=0; i < transforms.size(); i++) { 1296 for (int i=0; i < transforms.size(); i++) {
1290 // OK we have a trainable transform, we need to get input data for it. 1297 // OK we have a trainable transform, we need to get input data for it.
1291 if (transforms[i]->trainable) { 1298 if (transforms[i]->trainable) {
1292 - QList<TemplateList> copy = data; 1299 + QList<TemplateList> copy = separated;
1293 // Project from the start to the trainable stage. 1300 // Project from the start to the trainable stage.
1294 subProject(copy,i); 1301 subProject(copy,i);
1295 1302
@@ -1308,7 +1315,7 @@ public: @@ -1308,7 +1315,7 @@ public:
1308 in.append(src); 1315 in.append(src);
1309 TemplateList out; 1316 TemplateList out;
1310 CompositeTransform::project(in,out); 1317 CompositeTransform::project(in,out);
1311 - dst = out.first(); 1318 + if (!out.isEmpty()) dst = out.first();
1312 if (out.size() > 1) 1319 if (out.size() > 1)
1313 qDebug("Returning first output template only"); 1320 qDebug("Returning first output template only");
1314 } 1321 }
@@ -1319,7 +1326,7 @@ public: @@ -1319,7 +1326,7 @@ public:
1319 in.append(src); 1326 in.append(src);
1320 TemplateList out; 1327 TemplateList out;
1321 projectUpdate(in,out); 1328 projectUpdate(in,out);
1322 - dst = out.first(); 1329 + if (!out.isEmpty()) dst = out.first();
1323 if (out.size() > 1) 1330 if (out.size() > 1)
1324 qDebug("Returning first output template only"); 1331 qDebug("Returning first output template only");
1325 } 1332 }
scripts/brpy/__init__.py
@@ -58,7 +58,7 @@ def init_brpy(br_loc=&#39;/usr/local/lib&#39;): @@ -58,7 +58,7 @@ def init_brpy(br_loc=&#39;/usr/local/lib&#39;):
58 br.br_eval.argtypes = _string_args(3) 58 br.br_eval.argtypes = _string_args(3)
59 br.br_eval.restype = c_float 59 br.br_eval.restype = c_float
60 br.br_eval_classification.argtypes = _string_args(4) 60 br.br_eval_classification.argtypes = _string_args(4)
61 - br.br_eval_clustering.argtypes = _string_args(2) 61 + br.br_eval_clustering.argtypes = _string_args(3)
62 br.br_eval_detection.argtypes = _string_args(3) 62 br.br_eval_detection.argtypes = _string_args(3)
63 br.br_eval_detection.restype = c_float 63 br.br_eval_detection.restype = c_float
64 br.br_eval_landmarking.argtypes = _string_args(3) + [c_int, c_int] 64 br.br_eval_landmarking.argtypes = _string_args(3) + [c_int, c_int]
@@ -143,7 +143,8 @@ def init_brpy(br_loc=&#39;/usr/local/lib&#39;): @@ -143,7 +143,8 @@ def init_brpy(br_loc=&#39;/usr/local/lib&#39;):
143 br.br_make_gallery.restype = c_void_p 143 br.br_make_gallery.restype = c_void_p
144 br.br_load_from_gallery.argtypes = [c_void_p] 144 br.br_load_from_gallery.argtypes = [c_void_p]
145 br.br_load_from_gallery.restype = c_void_p 145 br.br_load_from_gallery.restype = c_void_p
146 - br.br_add_to_gallery.argtypes = [c_void_p, c_void_p] 146 + br.br_add_template_to_gallery.argtypes = [c_void_p, c_void_p]
  147 + br.br_add_template_list_to_gallery.argtypes = [c_void_p, c_void_p]
147 br.br_close_gallery.argtypes = [c_void_p] 148 br.br_close_gallery.argtypes = [c_void_p]
148 149
149 return br 150 return br