Commit 3808a6fe13088d7caf3584a0517f4f9dc4cb5f75
Merge branch 'master' into attributes
Showing
21 changed files
with
348 additions
and
54 deletions
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 &simmats) | @@ -100,7 +100,9 @@ Neighborhood getNeighborhood(const QStringList &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 &simmats) | @@ -115,8 +117,9 @@ Neighborhood getNeighborhood(const QStringList &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 &simmats) | @@ -157,7 +160,7 @@ Neighborhood getNeighborhood(const QStringList &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<int> &indicesA, const QVector<int> &indicesB) | @@ -275,13 +278,14 @@ float jaccardIndex(const QVector<int> &indicesA, const QVector<int> &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<float> Common::linspace(float start, float stop, int n) { | @@ -76,3 +76,11 @@ QList<float> 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<T> Downsample(V<T> vals, int k) | @@ -335,6 +335,10 @@ V<T> Downsample(V<T> 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 &operator>>(QDataStream &stream, Eigen::Matrix< _Scalar, _Row | @@ -67,4 +67,55 @@ inline QDataStream &operator>>(QDataStream &stream, Eigen::Matrix< _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 &predictedGallery, const QString &truthGalle | @@ -572,6 +572,7 @@ float EvalLandmarking(const QString &predictedGallery, const QString &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 &predictedGallery, const QString &truthGalle | @@ -579,7 +580,10 @@ float EvalLandmarking(const QString &predictedGallery, const QString &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 &predictedGallery, const QString &truthGalle | @@ -588,6 +592,7 @@ float EvalLandmarking(const QString &predictedGallery, const QString &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 &predictedGallery, const QString &truthGalle | @@ -605,6 +610,8 @@ float EvalLandmarking(const QString &predictedGallery, const QString &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 &file, QStringList &lines) | @@ -81,7 +82,7 @@ void readFile(const QString &file, QStringList &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 &value) | @@ -137,6 +137,8 @@ QVariant File::parse(const QString &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 ®exp, words[1].mid(1, words[1].size()-2).split(",")) | 743 | foreach (const QString ®exp, 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 &templates, int classes, int i | @@ -67,6 +68,11 @@ static TemplateList Downsample(const TemplateList &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='/usr/local/lib'): | @@ -58,7 +58,7 @@ def init_brpy(br_loc='/usr/local/lib'): | ||
| 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='/usr/local/lib'): | @@ -143,7 +143,8 @@ def init_brpy(br_loc='/usr/local/lib'): | ||
| 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 |