Commit d2de26db54ebbcebb3634a3c7a10f35993538007
Merge remote-tracking branch 'origin/master' into liblinear
Showing
25 changed files
with
583 additions
and
244 deletions
app/br/br.cpp
| @@ -163,8 +163,8 @@ public: | @@ -163,8 +163,8 @@ public: | ||
| 163 | check((parc >= 2) && (parc <= 6), "Incorrect parameter count for 'evalDetection'."); | 163 | check((parc >= 2) && (parc <= 6), "Incorrect parameter count for 'evalDetection'."); |
| 164 | br_eval_detection(parv[0], parv[1], parc >= 3 ? parv[2] : "", parc >= 4 ? atoi(parv[3]) : 0, parc >= 5 ? atoi(parv[4]) : 0, parc == 6 ? atoi(parv[5]) : 0); | 164 | br_eval_detection(parv[0], parv[1], parc >= 3 ? parv[2] : "", parc >= 4 ? atoi(parv[3]) : 0, parc >= 5 ? atoi(parv[4]) : 0, parc == 6 ? atoi(parv[5]) : 0); |
| 165 | } else if (!strcmp(fun, "evalLandmarking")) { | 165 | } else if (!strcmp(fun, "evalLandmarking")) { |
| 166 | - check((parc >= 2) && (parc <= 5), "Incorrect parameter count for 'evalLandmarking'."); | ||
| 167 | - br_eval_landmarking(parv[0], parv[1], parc >= 3 ? parv[2] : "", parc >= 4 ? atoi(parv[3]) : 0, parc >= 5 ? atoi(parv[4]) : 1); | 166 | + check((parc >= 2) && (parc <= 7), "Incorrect parameter count for 'evalLandmarking'."); |
| 167 | + br_eval_landmarking(parv[0], parv[1], parc >= 3 ? parv[2] : "", parc >= 4 ? atoi(parv[3]) : 0, parc >= 5 ? atoi(parv[4]) : 1, parc >= 6 ? atoi(parv[5]) : 0, parc >= 7 ? atoi(parv[6]) : 5); | ||
| 168 | } else if (!strcmp(fun, "evalRegression")) { | 168 | } else if (!strcmp(fun, "evalRegression")) { |
| 169 | check(parc >= 2 && parc <= 4, "Incorrect parameter count for 'evalRegression'."); | 169 | check(parc >= 2 && parc <= 4, "Incorrect parameter count for 'evalRegression'."); |
| 170 | br_eval_regression(parv[0], parv[1], parc >= 3 ? parv[2] : "", parc >= 4 ? parv[3] : ""); | 170 | br_eval_regression(parv[0], parv[1], parc >= 3 ? parv[2] : "", parc >= 4 ? parv[3] : ""); |
| @@ -264,7 +264,7 @@ private: | @@ -264,7 +264,7 @@ private: | ||
| 264 | "-evalClassification <predicted_gallery> <truth_gallery> <predicted property name> <ground truth proprty name>\n" | 264 | "-evalClassification <predicted_gallery> <truth_gallery> <predicted property name> <ground truth proprty name>\n" |
| 265 | "-evalClustering <clusters> <gallery>\n" | 265 | "-evalClustering <clusters> <gallery>\n" |
| 266 | "-evalDetection <predicted_gallery> <truth_gallery> [{csv}] [{normalize}] [{minSize}]\n" | 266 | "-evalDetection <predicted_gallery> <truth_gallery> [{csv}] [{normalize}] [{minSize}]\n" |
| 267 | - "-evalLandmarking <predicted_gallery> <truth_gallery> [{csv} [<normalization_index_a> <normalization_index_b>]]\n" | 267 | + "-evalLandmarking <predicted_gallery> <truth_gallery> [{csv} [<normalization_index_a> <normalization_index_b>] [sample_index] [total_examples]]\n" |
| 268 | "-evalRegression <predicted_gallery> <truth_gallery> <predicted property name> <ground truth property name>\n" | 268 | "-evalRegression <predicted_gallery> <truth_gallery> <predicted property name> <ground truth property name>\n" |
| 269 | "-assertEval <simmat> <mask> <accuracy>\n" | 269 | "-assertEval <simmat> <mask> <accuracy>\n" |
| 270 | "-plotDetection <file> ... <file> {destination}\n" | 270 | "-plotDetection <file> ... <file> {destination}\n" |
openbr/core/common.cpp
| @@ -16,21 +16,32 @@ | @@ -16,21 +16,32 @@ | ||
| 16 | 16 | ||
| 17 | #include "common.h" | 17 | #include "common.h" |
| 18 | #include <QMutex> | 18 | #include <QMutex> |
| 19 | +#include <RandomLib/Random.hpp> | ||
| 19 | 20 | ||
| 20 | using namespace std; | 21 | using namespace std; |
| 21 | 22 | ||
| 23 | +static RandomLib::Random g_rand; | ||
| 24 | +static QMutex rngLock; | ||
| 25 | + | ||
| 22 | /**** GLOBAL ****/ | 26 | /**** GLOBAL ****/ |
| 23 | void Common::seedRNG() { | 27 | void Common::seedRNG() { |
| 24 | - static QMutex seedControl; | ||
| 25 | - QMutexLocker lock(&seedControl); | 28 | + QMutexLocker lock(&rngLock); |
| 26 | 29 | ||
| 27 | static bool seeded = false; | 30 | static bool seeded = false; |
| 28 | if (!seeded) { | 31 | if (!seeded) { |
| 29 | srand(0); // We seed with 0 instead of time(NULL) to have reproducible randomness | 32 | srand(0); // We seed with 0 instead of time(NULL) to have reproducible randomness |
| 30 | seeded = true; | 33 | seeded = true; |
| 34 | + g_rand.Reseed(0); | ||
| 31 | } | 35 | } |
| 32 | } | 36 | } |
| 33 | 37 | ||
| 38 | +double Common::randN() | ||
| 39 | +{ | ||
| 40 | + QMutexLocker lock(&rngLock); | ||
| 41 | + | ||
| 42 | + return g_rand.FloatN(); | ||
| 43 | +} | ||
| 44 | + | ||
| 34 | QList<int> Common::RandSample(int n, int max, int min, bool unique) | 45 | QList<int> Common::RandSample(int n, int max, int min, bool unique) |
| 35 | { | 46 | { |
| 36 | QList<int> samples; samples.reserve(n); | 47 | QList<int> samples; samples.reserve(n); |
openbr/core/common.h
| @@ -220,6 +220,9 @@ double KernelDensityEstimation(const V<T> &vals, double x, double h) | @@ -220,6 +220,9 @@ double KernelDensityEstimation(const V<T> &vals, double x, double h) | ||
| 220 | return y / (vals.size() * h); | 220 | return y / (vals.size() * h); |
| 221 | } | 221 | } |
| 222 | 222 | ||
| 223 | +// Return a random number, uniformly distributed over 0,1 | ||
| 224 | +double randN(); | ||
| 225 | + | ||
| 223 | /*! | 226 | /*! |
| 224 | * \brief Returns a vector of n integers sampled in the range <min, max]. | 227 | * \brief Returns a vector of n integers sampled in the range <min, max]. |
| 225 | * | 228 | * |
| @@ -236,19 +239,14 @@ QList<int> RandSample(int n, const QSet<int> &values, bool unique = false); | @@ -236,19 +239,14 @@ QList<int> RandSample(int n, const QSet<int> &values, bool unique = false); | ||
| 236 | template <typename T> | 239 | template <typename T> |
| 237 | QList<int> RandSample(int n, const QList<T> &weights, bool unique = false) | 240 | QList<int> RandSample(int n, const QList<T> &weights, bool unique = false) |
| 238 | { | 241 | { |
| 239 | - static bool seeded = false; | ||
| 240 | - if (!seeded) { | ||
| 241 | - srand(time(NULL)); | ||
| 242 | - seeded = true; | ||
| 243 | - } | ||
| 244 | - | ||
| 245 | QList<T> cdf = CumSum(weights); | 242 | QList<T> cdf = CumSum(weights); |
| 246 | for (int i=0; i<cdf.size(); i++) // Normalize cdf | 243 | for (int i=0; i<cdf.size(); i++) // Normalize cdf |
| 247 | cdf[i] = cdf[i] / cdf.last(); | 244 | cdf[i] = cdf[i] / cdf.last(); |
| 248 | 245 | ||
| 249 | QList<int> samples; samples.reserve(n); | 246 | QList<int> samples; samples.reserve(n); |
| 250 | while (samples.size() < n) { | 247 | while (samples.size() < n) { |
| 251 | - T r = (T)rand() / (T)RAND_MAX; | 248 | + T r = randN(); |
| 249 | + | ||
| 252 | for (int j=0; j<weights.size(); j++) { | 250 | for (int j=0; j<weights.size(); j++) { |
| 253 | if ((r >= cdf[j]) && (r <= cdf[j+1])) { | 251 | if ((r >= cdf[j]) && (r <= cdf[j+1])) { |
| 254 | if (!unique || !samples.contains(j)) | 252 | if (!unique || !samples.contains(j)) |
openbr/core/core.cpp
| @@ -110,9 +110,11 @@ struct AlgorithmCore | @@ -110,9 +110,11 @@ struct AlgorithmCore | ||
| 110 | 110 | ||
| 111 | void store(const QString &model) const | 111 | void store(const QString &model) const |
| 112 | { | 112 | { |
| 113 | - // Create stream | ||
| 114 | - QByteArray data; | ||
| 115 | - QDataStream out(&data, QFile::WriteOnly); | 113 | + QtUtils::BlockCompression compressedWrite; |
| 114 | + QFile outFile(model); | ||
| 115 | + compressedWrite.setBasis(&outFile); | ||
| 116 | + QDataStream out(&compressedWrite); | ||
| 117 | + compressedWrite.open(QFile::WriteOnly); | ||
| 116 | 118 | ||
| 117 | // Serialize algorithm to stream | 119 | // Serialize algorithm to stream |
| 118 | transform->serialize(out); | 120 | transform->serialize(out); |
| @@ -131,18 +133,16 @@ struct AlgorithmCore | @@ -131,18 +133,16 @@ struct AlgorithmCore | ||
| 131 | if (mode == TransformCompare) | 133 | if (mode == TransformCompare) |
| 132 | comparison->serialize(out); | 134 | comparison->serialize(out); |
| 133 | 135 | ||
| 134 | - // Compress and save to file | ||
| 135 | - QtUtils::writeFile(model, data, -1); | 136 | + compressedWrite.close(); |
| 136 | } | 137 | } |
| 137 | 138 | ||
| 138 | void load(const QString &model) | 139 | void load(const QString &model) |
| 139 | { | 140 | { |
| 140 | - // Load from file and decompress | ||
| 141 | - QByteArray data; | ||
| 142 | - QtUtils::readFile(model, data, true); | ||
| 143 | - | ||
| 144 | - // Create stream | ||
| 145 | - QDataStream in(&data, QFile::ReadOnly); | 141 | + QtUtils::BlockCompression compressedRead; |
| 142 | + QFile inFile(model); | ||
| 143 | + compressedRead.setBasis(&inFile); | ||
| 144 | + QDataStream in(&compressedRead); | ||
| 145 | + compressedRead.open(QFile::ReadOnly); | ||
| 146 | 146 | ||
| 147 | // Load algorithm | 147 | // Load algorithm |
| 148 | transform = QSharedPointer<Transform>(Transform::deserialize(in)); | 148 | transform = QSharedPointer<Transform>(Transform::deserialize(in)); |
openbr/core/eigenutils.cpp
| @@ -4,80 +4,15 @@ | @@ -4,80 +4,15 @@ | ||
| 4 | using namespace Eigen; | 4 | using namespace Eigen; |
| 5 | using namespace cv; | 5 | using namespace cv; |
| 6 | 6 | ||
| 7 | -//Helper function to quickly write eigen matrix to disk. Not efficient. | ||
| 8 | -void writeEigen(MatrixXf X, QString filename) { | ||
| 9 | - Mat m(X.rows(),X.cols(),CV_32FC1); | ||
| 10 | - for (int i = 0; i < X.rows(); i++) { | ||
| 11 | - for (int j = 0; j < X.cols(); j++) { | ||
| 12 | - m.at<float>(i,j) = X(i,j); | ||
| 13 | - } | ||
| 14 | - } | ||
| 15 | - QScopedPointer<br::Format> format(br::Factory<br::Format>::make(filename)); | ||
| 16 | - format->write(br::Template(m)); | ||
| 17 | -} | ||
| 18 | - | ||
| 19 | -void writeEigen(MatrixXd X, QString filename) { | ||
| 20 | - Mat m(X.rows(),X.cols(),CV_32FC1); | ||
| 21 | - for (int i = 0; i < X.rows(); i++) { | ||
| 22 | - for (int j = 0; j < X.cols(); j++) { | ||
| 23 | - m.at<float>(i,j) = (float)X(i,j); | ||
| 24 | - } | ||
| 25 | - } | ||
| 26 | - QScopedPointer<br::Format> format(br::Factory<br::Format>::make(filename)); | ||
| 27 | - format->write(br::Template(m)); | ||
| 28 | -} | ||
| 29 | - | ||
| 30 | -void writeEigen(VectorXd X, QString filename) { | ||
| 31 | - Mat m(X.size(),1,CV_32FC1); | ||
| 32 | - for (int i = 0; i < X.rows(); i++) { | ||
| 33 | - m.at<float>(i,0) = (float)X(i); | ||
| 34 | - } | ||
| 35 | - QScopedPointer<br::Format> format(br::Factory<br::Format>::make(filename)); | ||
| 36 | - format->write(br::Template(m)); | ||
| 37 | -} | ||
| 38 | - | ||
| 39 | -void writeEigen(VectorXf X, QString filename) { | ||
| 40 | - Mat m(X.size(),1,CV_32FC1); | ||
| 41 | - for (int i = 0; i < X.rows(); i++) { | ||
| 42 | - m.at<float>(i,0) = X(i); | ||
| 43 | - } | ||
| 44 | - QScopedPointer<br::Format> format(br::Factory<br::Format>::make(filename)); | ||
| 45 | - format->write(br::Template(m)); | ||
| 46 | -} | ||
| 47 | - | ||
| 48 | -void printEigen(Eigen::MatrixXd X) { | ||
| 49 | - for (int i = 0; i < X.rows(); i++) { | ||
| 50 | - QString str; | ||
| 51 | - for (int j = 0; j < X.cols(); j++) { | ||
| 52 | - str.append(QString::number(X(i,j)) + " "); | ||
| 53 | - } | ||
| 54 | - qDebug() << str; | ||
| 55 | - } | ||
| 56 | -} | ||
| 57 | -void printEigen(Eigen::MatrixXf X) { | ||
| 58 | - for (int i = 0; i < X.rows(); i++) { | ||
| 59 | - QString str; | ||
| 60 | - for (int j = 0; j < X.cols(); j++) { | ||
| 61 | - str.append(QString::number(X(i,j)) + " "); | ||
| 62 | - } | ||
| 63 | - qDebug() << str; | ||
| 64 | - } | ||
| 65 | -} | ||
| 66 | - | ||
| 67 | -void printSize(Eigen::MatrixXf X) { | 7 | +void EigenUtils::printSize(Eigen::MatrixXf X) { |
| 68 | qDebug() << "Rows=" << X.rows() << "\tCols=" << X.cols(); | 8 | qDebug() << "Rows=" << X.rows() << "\tCols=" << X.cols(); |
| 69 | } | 9 | } |
| 70 | 10 | ||
| 71 | -float eigMean(const Eigen::MatrixXf& x) { | ||
| 72 | - return x.array().sum() / (x.rows() * x.cols()); | 11 | +float EigenUtils::stddev(const Eigen::MatrixXf& x) { |
| 12 | + return sqrt((x.array() - x.mean()).pow(2).sum() / (x.cols() * x.rows())); | ||
| 73 | } | 13 | } |
| 74 | 14 | ||
| 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 | -} | ||
| 79 | - | ||
| 80 | -MatrixXf removeRowCol(const MatrixXf X, int row, int col) { | 15 | +MatrixXf EigenUtils::removeRowCol(const MatrixXf X, int row, int col) { |
| 81 | MatrixXf Y(X.rows() - 1,X.cols() - 1); | 16 | MatrixXf Y(X.rows() - 1,X.cols() - 1); |
| 82 | 17 | ||
| 83 | for (int i1 = 0, i2 = 0; i1 < X.rows(); i1++) { | 18 | for (int i1 = 0, i2 = 0; i1 < X.rows(); i1++) { |
| @@ -96,7 +31,7 @@ MatrixXf removeRowCol(const MatrixXf X, int row, int col) { | @@ -96,7 +31,7 @@ MatrixXf removeRowCol(const MatrixXf X, int row, int col) { | ||
| 96 | return Y; | 31 | return Y; |
| 97 | } | 32 | } |
| 98 | 33 | ||
| 99 | -MatrixXf pointsToMatrix(const QList<QPointF> points, bool isAffine) { | 34 | +MatrixXf EigenUtils::pointsToMatrix(const QList<QPointF> points, bool isAffine) { |
| 100 | MatrixXf P(points.size(), isAffine ? 3 : 2); | 35 | MatrixXf P(points.size(), isAffine ? 3 : 2); |
| 101 | for (int i = 0; i < points.size(); i++) { | 36 | for (int i = 0; i < points.size(); i++) { |
| 102 | P(i, 0) = points[i].x(); | 37 | P(i, 0) = points[i].x(); |
| @@ -107,7 +42,7 @@ MatrixXf pointsToMatrix(const QList<QPointF> points, bool isAffine) { | @@ -107,7 +42,7 @@ MatrixXf pointsToMatrix(const QList<QPointF> points, bool isAffine) { | ||
| 107 | return P; | 42 | return P; |
| 108 | } | 43 | } |
| 109 | 44 | ||
| 110 | -QList<QPointF> matrixToPoints(const Eigen::MatrixXf P) { | 45 | +QList<QPointF> EigenUtils::matrixToPoints(const Eigen::MatrixXf P) { |
| 111 | QList<QPointF> points; | 46 | QList<QPointF> points; |
| 112 | for (int i = 0; i < P.rows(); i++) | 47 | for (int i = 0; i < P.rows(); i++) |
| 113 | points.append(QPointF(P(i, 0), P(i, 1))); | 48 | points.append(QPointF(P(i, 0), P(i, 1))); |
| @@ -115,7 +50,7 @@ QList<QPointF> matrixToPoints(const Eigen::MatrixXf P) { | @@ -115,7 +50,7 @@ QList<QPointF> matrixToPoints(const Eigen::MatrixXf P) { | ||
| 115 | } | 50 | } |
| 116 | 51 | ||
| 117 | //Converts x y points in a single vector to two column matrix | 52 | //Converts x y points in a single vector to two column matrix |
| 118 | -Eigen::MatrixXf vectorToMatrix(const Eigen::MatrixXf vector) { | 53 | +Eigen::MatrixXf EigenUtils::vectorToMatrix(const Eigen::MatrixXf vector) { |
| 119 | int n = vector.rows(); | 54 | int n = vector.rows(); |
| 120 | Eigen::MatrixXf matrix(n / 2, 2); | 55 | Eigen::MatrixXf matrix(n / 2, 2); |
| 121 | for (int i = 0; i < n / 2; i++) { | 56 | for (int i = 0; i < n / 2; i++) { |
| @@ -126,7 +61,7 @@ Eigen::MatrixXf vectorToMatrix(const Eigen::MatrixXf vector) { | @@ -126,7 +61,7 @@ Eigen::MatrixXf vectorToMatrix(const Eigen::MatrixXf vector) { | ||
| 126 | return matrix; | 61 | return matrix; |
| 127 | } | 62 | } |
| 128 | 63 | ||
| 129 | -Eigen::MatrixXf matrixToVector(const Eigen::MatrixXf matrix) { | 64 | +Eigen::MatrixXf EigenUtils::matrixToVector(const Eigen::MatrixXf matrix) { |
| 130 | int n2 = matrix.rows(); | 65 | int n2 = matrix.rows(); |
| 131 | Eigen::MatrixXf vector(n2 * 2, 1); | 66 | Eigen::MatrixXf vector(n2 * 2, 1); |
| 132 | for (int i = 0; i < n2; i++) { | 67 | for (int i = 0; i < n2; i++) { |
| @@ -136,12 +71,3 @@ Eigen::MatrixXf matrixToVector(const Eigen::MatrixXf matrix) { | @@ -136,12 +71,3 @@ Eigen::MatrixXf matrixToVector(const Eigen::MatrixXf matrix) { | ||
| 136 | } | 71 | } |
| 137 | return vector; | 72 | return vector; |
| 138 | } | 73 | } |
| 139 | - | ||
| 140 | -Eigen::MatrixXf toEigen(const Mat m) { | ||
| 141 | - if (m.type() != CV_32F) | ||
| 142 | - qFatal("Mat to Eigen Converstation only supports CV_32F"); | ||
| 143 | - | ||
| 144 | - Eigen::MatrixXf data(m.rows, m.cols); | ||
| 145 | - return Eigen::Map<const Eigen::MatrixXf>(m.ptr<float>(), m.rows, m.cols); | ||
| 146 | -} | ||
| 147 | - |
openbr/core/eigenutils.h
| @@ -18,32 +18,74 @@ | @@ -18,32 +18,74 @@ | ||
| 18 | #define EIGENUTILS_H | 18 | #define EIGENUTILS_H |
| 19 | 19 | ||
| 20 | #include <QDataStream> | 20 | #include <QDataStream> |
| 21 | +#include <QDebug> | ||
| 21 | #include <Eigen/Core> | 22 | #include <Eigen/Core> |
| 22 | #include <assert.h> | 23 | #include <assert.h> |
| 23 | 24 | ||
| 25 | +#include <opencv2/core/eigen.hpp> | ||
| 24 | #include <opencv2/core/core.hpp> | 26 | #include <opencv2/core/core.hpp> |
| 25 | 27 | ||
| 26 | -void writeEigen(Eigen::MatrixXf X, QString filename); | ||
| 27 | -void writeEigen(Eigen::MatrixXd X, QString filename); | ||
| 28 | -void writeEigen(Eigen::VectorXd X, QString filename); | ||
| 29 | -void writeEigen(Eigen::VectorXf X, QString filename); | ||
| 30 | -void printEigen(Eigen::MatrixXd X); | ||
| 31 | -void printEigen(Eigen::MatrixXf X); | ||
| 32 | -void printSize(Eigen::MatrixXf X); | 28 | +#include "openbr/core/qtutils.h" |
| 33 | 29 | ||
| 34 | -//Converts x y points in a single vector to two column matrix | ||
| 35 | -Eigen::MatrixXf vectorToMatrix(const Eigen::MatrixXf vector); | ||
| 36 | -Eigen::MatrixXf matrixToVector(const Eigen::MatrixXf matrix); | 30 | +namespace EigenUtils |
| 31 | +{ | ||
| 32 | + template<typename _Scalar, int _Rows, int _Cols, int _Options, int _MaxRows, int _MaxCols> | ||
| 33 | + QString matrixToString(const Eigen::Matrix< _Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols > &mat) | ||
| 34 | + { | ||
| 35 | + QString result; | ||
| 36 | + if (mat.rows() > 1) result += "{ "; | ||
| 37 | + for (int r=0; r<mat.rows(); r++) { | ||
| 38 | + if ((mat.rows() > 1) && (r > 0)) result += " "; | ||
| 39 | + if (mat.cols() > 1) result += "["; | ||
| 40 | + for (int c=0; c<mat.cols(); c++) { | ||
| 41 | + result += QString::number(mat(r, c)); | ||
| 42 | + if (c < mat.cols() - 1) result += ", "; | ||
| 43 | + } | ||
| 44 | + if (mat.cols() > 1) result += "]"; | ||
| 45 | + if (r < mat.rows()-1) result += "\n"; | ||
| 46 | + } | ||
| 47 | + if (mat.rows() > 1) result += " }"; | ||
| 48 | + return result; | ||
| 49 | + } | ||
| 50 | + | ||
| 51 | + template<typename _Scalar, int _Rows, int _Cols, int _Options, int _MaxRows, int _MaxCols> | ||
| 52 | + void writeMatrix(const Eigen::Matrix< _Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols > &mat, const QString &filename) | ||
| 53 | + { | ||
| 54 | + int r = mat.rows(); | ||
| 55 | + int c = mat.cols(); | ||
| 56 | + | ||
| 57 | + _Scalar *data = new _Scalar[r*c]; | ||
| 58 | + for (int i=0; i<r; i++) | ||
| 59 | + for (int j=0; j<c; j++) | ||
| 60 | + data[i*c+j] = mat(i, j); | ||
| 61 | + int bytes = r*c*sizeof(_Scalar); | ||
| 62 | + QByteArray byteArray((const char*)data,bytes); | ||
| 63 | + QtUtils::writeFile(filename,byteArray); | ||
| 64 | + } | ||
| 65 | + | ||
| 66 | + void printSize(Eigen::MatrixXf X); | ||
| 37 | 67 | ||
| 38 | -//Remove row and column from the matrix: | ||
| 39 | -Eigen::MatrixXf removeRowCol(const Eigen::MatrixXf X, int row, int col); | 68 | + // Converts x y points in a single vector to two column matrix |
| 69 | + Eigen::MatrixXf vectorToMatrix(const Eigen::MatrixXf vector); | ||
| 70 | + Eigen::MatrixXf matrixToVector(const Eigen::MatrixXf matrix); | ||
| 40 | 71 | ||
| 41 | -//Convert a point list into a matrix: | ||
| 42 | -Eigen::MatrixXf pointsToMatrix(const QList<QPointF> points, bool isAffine=false); | ||
| 43 | -QList<QPointF> matrixToPoints(const Eigen::MatrixXf P); | 72 | + // Remove row and column from the matrix: |
| 73 | + Eigen::MatrixXf removeRowCol(const Eigen::MatrixXf X, int row, int col); | ||
| 44 | 74 | ||
| 45 | -//Convert cv::Mat to Eigen | ||
| 46 | -Eigen::MatrixXf toEigen(const cv::Mat m); | 75 | + // Convert a point list into a matrix: |
| 76 | + Eigen::MatrixXf pointsToMatrix(const QList<QPointF> points, bool isAffine=false); | ||
| 77 | + QList<QPointF> matrixToPoints(const Eigen::MatrixXf P); | ||
| 78 | + | ||
| 79 | + // Compute the element-wise standard deviation | ||
| 80 | + float stddev(const Eigen::MatrixXf& x); | ||
| 81 | +} | ||
| 82 | + | ||
| 83 | +template<typename _Scalar, int _Rows, int _Cols, int _Options, int _MaxRows, int _MaxCols> | ||
| 84 | +inline QDebug operator<<(QDebug dbg, const Eigen::Matrix< _Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols > &mat) | ||
| 85 | +{ | ||
| 86 | + dbg.nospace() << EigenUtils::matrixToString(mat); | ||
| 87 | + return dbg.space(); | ||
| 88 | +} | ||
| 47 | 89 | ||
| 48 | template<typename _Scalar, int _Rows, int _Cols, int _Options, int _MaxRows, int _MaxCols> | 90 | template<typename _Scalar, int _Rows, int _Cols, int _Options, int _MaxRows, int _MaxCols> |
| 49 | inline QDataStream &operator<<(QDataStream &stream, const Eigen::Matrix< _Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols > &mat) | 91 | inline QDataStream &operator<<(QDataStream &stream, const Eigen::Matrix< _Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols > &mat) |
| @@ -83,56 +125,4 @@ inline QDataStream &operator>>(QDataStream &stream, Eigen::Matrix< _Scalar, _Row | @@ -83,56 +125,4 @@ inline QDataStream &operator>>(QDataStream &stream, Eigen::Matrix< _Scalar, _Row | ||
| 83 | return stream; | 125 | return stream; |
| 84 | } | 126 | } |
| 85 | 127 | ||
| 86 | -/*Compute the mean of the each column (dim == 1) or row (dim == 2) | ||
| 87 | - of the matrix*/ | ||
| 88 | -template<typename T> | ||
| 89 | -Eigen::MatrixBase<T> eigMean(const Eigen::MatrixBase<T>& x,int dim) | ||
| 90 | -{ | ||
| 91 | - if (dim == 1) { | ||
| 92 | - Eigen::MatrixBase<T> y(1,x.cols()); | ||
| 93 | - for (int i = 0; i < x.cols(); i++) | ||
| 94 | - y(i) = x.col(i).sum() / x.rows(); | ||
| 95 | - return y; | ||
| 96 | - } else if (dim == 2) { | ||
| 97 | - Eigen::MatrixBase<T> y(x.rows(),1); | ||
| 98 | - for (int i = 0; i < x.rows(); i++) | ||
| 99 | - y(i) = x.row(i).sum() / x.cols(); | ||
| 100 | - return y; | ||
| 101 | - } | ||
| 102 | - qFatal("A matrix can only have two dimensions"); | ||
| 103 | -} | ||
| 104 | - | ||
| 105 | -/*Compute the element-wise mean*/ | ||
| 106 | -float eigMean(const Eigen::MatrixXf& x); | ||
| 107 | -/*Compute the element-wise mean*/ | ||
| 108 | -float eigStd(const Eigen::MatrixXf& x); | ||
| 109 | - | ||
| 110 | -/*Compute the std dev of the each column (dim == 1) or row (dim == 2) | ||
| 111 | - of the matrix*/ | ||
| 112 | -template<typename T> | ||
| 113 | -Eigen::MatrixBase<T> eigStd(const Eigen::MatrixBase<T>& x,int dim) | ||
| 114 | -{ | ||
| 115 | - Eigen::MatrixBase<T> mean = eigMean(x, dim); | ||
| 116 | - if (dim == 1) { | ||
| 117 | - Eigen::MatrixBase<T> y(1,x.cols()); | ||
| 118 | - for (int i = 0; i < x.cols(); i++) { | ||
| 119 | - T value = 0; | ||
| 120 | - for (int j = 0; j < x.rows(); j++) | ||
| 121 | - value += pow(y(j, i) - mean(i), 2); | ||
| 122 | - y(i) = sqrt(value / (x.rows() - 1)); | ||
| 123 | - } | ||
| 124 | - return y; | ||
| 125 | - } else if (dim == 2) { | ||
| 126 | - Eigen::MatrixBase<T> y(x.rows(),1); | ||
| 127 | - for (int i = 0; i < x.rows(); i++) { | ||
| 128 | - T value = 0; | ||
| 129 | - for (int j = 0; j < x.cols(); j++) | ||
| 130 | - value += pow(y(i, j) - mean(j), 2); | ||
| 131 | - y(i) = sqrt(value / (x.cols() - 1)); | ||
| 132 | - } | ||
| 133 | - return y; | ||
| 134 | - } | ||
| 135 | - qFatal("A matrix can only have two dimensions"); | ||
| 136 | -} | ||
| 137 | - | ||
| 138 | #endif // EIGENUTILS_H | 128 | #endif // EIGENUTILS_H |
openbr/core/eval.cpp
| @@ -18,6 +18,7 @@ | @@ -18,6 +18,7 @@ | ||
| 18 | #include "eval.h" | 18 | #include "eval.h" |
| 19 | #include "openbr/core/common.h" | 19 | #include "openbr/core/common.h" |
| 20 | #include "openbr/core/qtutils.h" | 20 | #include "openbr/core/qtutils.h" |
| 21 | +#include "openbr/core/opencvutils.h" | ||
| 21 | #include <QMapIterator> | 22 | #include <QMapIterator> |
| 22 | 23 | ||
| 23 | using namespace cv; | 24 | using namespace cv; |
| @@ -1068,16 +1069,25 @@ float EvalDetection(const QString &predictedGallery, const QString &truthGallery | @@ -1068,16 +1069,25 @@ float EvalDetection(const QString &predictedGallery, const QString &truthGallery | ||
| 1068 | return averageOverlap; | 1069 | return averageOverlap; |
| 1069 | } | 1070 | } |
| 1070 | 1071 | ||
| 1071 | -float EvalLandmarking(const QString &predictedGallery, const QString &truthGallery, const QString &csv, int normalizationIndexA, int normalizationIndexB) | 1072 | +static void projectAndWrite(Transform *t, const Template &src, const QString &filePath) |
| 1073 | +{ | ||
| 1074 | + Template dst; | ||
| 1075 | + t->project(src,dst); | ||
| 1076 | + OpenCVUtils::saveImage(dst.m(),filePath); | ||
| 1077 | +} | ||
| 1078 | + | ||
| 1079 | +float EvalLandmarking(const QString &predictedGallery, const QString &truthGallery, const QString &csv, int normalizationIndexA, int normalizationIndexB, int sampleIndex, int totalExamples) | ||
| 1072 | { | 1080 | { |
| 1073 | qDebug("Evaluating landmarking of %s against %s", qPrintable(predictedGallery), qPrintable(truthGallery)); | 1081 | qDebug("Evaluating landmarking of %s against %s", qPrintable(predictedGallery), qPrintable(truthGallery)); |
| 1074 | - const TemplateList predicted(TemplateList::fromGallery(predictedGallery)); | ||
| 1075 | - const TemplateList truth(TemplateList::fromGallery(truthGallery)); | ||
| 1076 | - const QStringList predictedNames = File::get<QString>(predicted, "name"); | ||
| 1077 | - const QStringList truthNames = File::get<QString>(truth, "name"); | 1082 | + TemplateList predicted(TemplateList::fromGallery(predictedGallery)); |
| 1083 | + TemplateList truth(TemplateList::fromGallery(truthGallery)); | ||
| 1084 | + QStringList predictedNames = File::get<QString>(predicted, "name"); | ||
| 1085 | + QStringList truthNames = File::get<QString>(truth, "name"); | ||
| 1078 | 1086 | ||
| 1079 | int skipped = 0; | 1087 | int skipped = 0; |
| 1080 | QList< QList<float> > pointErrors; | 1088 | QList< QList<float> > pointErrors; |
| 1089 | + QList<float> imageErrors; | ||
| 1090 | + QList<float> normalizedLengths; | ||
| 1081 | for (int i=0; i<predicted.size(); i++) { | 1091 | for (int i=0; i<predicted.size(); i++) { |
| 1082 | const QString &predictedName = predictedNames[i]; | 1092 | const QString &predictedName = predictedNames[i]; |
| 1083 | const int truthIndex = truthNames.indexOf(predictedName); | 1093 | const int truthIndex = truthNames.indexOf(predictedName); |
| @@ -1085,39 +1095,94 @@ float EvalLandmarking(const QString &predictedGallery, const QString &truthGalle | @@ -1085,39 +1095,94 @@ float EvalLandmarking(const QString &predictedGallery, const QString &truthGalle | ||
| 1085 | const QList<QPointF> predictedPoints = predicted[i].file.points(); | 1095 | const QList<QPointF> predictedPoints = predicted[i].file.points(); |
| 1086 | const QList<QPointF> truthPoints = truth[truthIndex].file.points(); | 1096 | const QList<QPointF> truthPoints = truth[truthIndex].file.points(); |
| 1087 | if (predictedPoints.size() != truthPoints.size() || truthPoints.contains(QPointF(-1,-1))) { | 1097 | if (predictedPoints.size() != truthPoints.size() || truthPoints.contains(QPointF(-1,-1))) { |
| 1088 | - skipped++; | 1098 | + predicted.removeAt(i); |
| 1099 | + predictedNames.removeAt(i); | ||
| 1100 | + truth.removeAt(i); | ||
| 1101 | + truthNames.removeAt(i); | ||
| 1102 | + i--; skipped++; | ||
| 1089 | continue; | 1103 | continue; |
| 1090 | } | 1104 | } |
| 1105 | + | ||
| 1091 | while (pointErrors.size() < predictedPoints.size()) | 1106 | while (pointErrors.size() < predictedPoints.size()) |
| 1092 | pointErrors.append(QList<float>()); | 1107 | pointErrors.append(QList<float>()); |
| 1108 | + | ||
| 1109 | + // Want to know error for every image. | ||
| 1110 | + | ||
| 1093 | if (normalizationIndexA >= truthPoints.size()) qFatal("Normalization index A is out of range."); | 1111 | if (normalizationIndexA >= truthPoints.size()) qFatal("Normalization index A is out of range."); |
| 1094 | if (normalizationIndexB >= truthPoints.size()) qFatal("Normalization index B is out of range."); | 1112 | if (normalizationIndexB >= truthPoints.size()) qFatal("Normalization index B is out of range."); |
| 1095 | const float normalizedLength = QtUtils::euclideanLength(truthPoints[normalizationIndexB] - truthPoints[normalizationIndexA]); | 1113 | const float normalizedLength = QtUtils::euclideanLength(truthPoints[normalizationIndexB] - truthPoints[normalizationIndexA]); |
| 1096 | - for (int j=0; j<predictedPoints.size(); j++) | ||
| 1097 | - pointErrors[j].append(QtUtils::euclideanLength(predictedPoints[j] - truthPoints[j])/normalizedLength); | 1114 | + normalizedLengths.append(normalizedLength); |
| 1115 | + float totalError = 0; | ||
| 1116 | + for (int j=0; j<predictedPoints.size(); j++) { | ||
| 1117 | + float error = QtUtils::euclideanLength(predictedPoints[j] - truthPoints[j])/normalizedLength; | ||
| 1118 | + totalError += error; | ||
| 1119 | + pointErrors[j].append(error); | ||
| 1120 | + } | ||
| 1121 | + imageErrors.append(totalError/predictedPoints.size()); | ||
| 1098 | } | 1122 | } |
| 1099 | - qDebug() << "Skipped " << skipped << " files due to point size mismatch."; | 1123 | + |
| 1124 | + qDebug() << "Skipped" << skipped << "files due to point size mismatch."; | ||
| 1100 | 1125 | ||
| 1101 | QList<float> averagePointErrors; averagePointErrors.reserve(pointErrors.size()); | 1126 | QList<float> averagePointErrors; averagePointErrors.reserve(pointErrors.size()); |
| 1102 | - for (int i=0; i<pointErrors.size(); i++) { | ||
| 1103 | - std::sort(pointErrors[i].begin(), pointErrors[i].end()); | ||
| 1104 | - averagePointErrors.append(Common::Mean(pointErrors[i])); | ||
| 1105 | - } | ||
| 1106 | - const float averagePointError = Common::Mean(averagePointErrors); | ||
| 1107 | 1127 | ||
| 1108 | QStringList lines; | 1128 | QStringList lines; |
| 1109 | lines.append("Plot,X,Y"); | 1129 | lines.append("Plot,X,Y"); |
| 1130 | + | ||
| 1131 | + QtUtils::touchDir(QDir("landmarking_examples_truth")); | ||
| 1132 | + QtUtils::touchDir(QDir("landmarking_examples_predicted")); | ||
| 1133 | + | ||
| 1134 | + // Example | ||
| 1135 | + { | ||
| 1136 | + QScopedPointer<Transform> t(Transform::make("Open+Draw(verbose,rects=false,location=false)",NULL)); | ||
| 1137 | + | ||
| 1138 | + QString filePath = "landmarking_examples_truth/"+truth[sampleIndex].file.fileName(); | ||
| 1139 | + projectAndWrite(t.data(), truth[sampleIndex],filePath); | ||
| 1140 | + lines.append("Sample,"+filePath+","+QString::number(truth[sampleIndex].file.points().size())); | ||
| 1141 | + } | ||
| 1142 | + | ||
| 1143 | + // Get best and worst performing examples | ||
| 1144 | + QList< QPair<float,int> > exampleIndices = Common::Sort(imageErrors,true); | ||
| 1145 | + | ||
| 1146 | + QScopedPointer<Transform> t(Transform::make("Open+Draw(rects=false)",NULL)); | ||
| 1147 | + | ||
| 1148 | + for (int i=0; i<totalExamples; i++) { | ||
| 1149 | + QString filePath = "landmarking_examples_truth/"+truth[exampleIndices[i].second].file.fileName(); | ||
| 1150 | + projectAndWrite(t.data(), truth[exampleIndices[i].second],filePath); | ||
| 1151 | + lines.append("EXT,"+filePath+","+QString::number(exampleIndices[i].first)); | ||
| 1152 | + | ||
| 1153 | + filePath = "landmarking_examples_predicted/"+predicted[exampleIndices[i].second].file.fileName(); | ||
| 1154 | + projectAndWrite(t.data(), predicted[exampleIndices[i].second],filePath); | ||
| 1155 | + lines.append("EXP,"+filePath+","+QString::number(exampleIndices[i].first)); | ||
| 1156 | + } | ||
| 1157 | + | ||
| 1158 | + for (int i=exampleIndices.size()-1; i>exampleIndices.size()-totalExamples-1; i--) { | ||
| 1159 | + QString filePath = "landmarking_examples_truth/"+truth[exampleIndices[i].second].file.fileName(); | ||
| 1160 | + projectAndWrite(t.data(), truth[exampleIndices[i].second],filePath); | ||
| 1161 | + lines.append("EXT,"+filePath+","+QString::number(exampleIndices[i].first)); | ||
| 1162 | + | ||
| 1163 | + filePath = "landmarking_examples_predicted/"+predicted[exampleIndices[i].second].file.fileName(); | ||
| 1164 | + projectAndWrite(t.data(), predicted[exampleIndices[i].second],filePath); | ||
| 1165 | + lines.append("EXP,"+filePath+","+QString::number(exampleIndices[i].first)); | ||
| 1166 | + } | ||
| 1167 | + | ||
| 1110 | for (int i=0; i<pointErrors.size(); i++) { | 1168 | for (int i=0; i<pointErrors.size(); i++) { |
| 1169 | + std::sort(pointErrors[i].begin(), pointErrors[i].end()); | ||
| 1170 | + averagePointErrors.append(Common::Mean(pointErrors[i])); | ||
| 1111 | const QList<float> &pointError = pointErrors[i]; | 1171 | const QList<float> &pointError = pointErrors[i]; |
| 1112 | const int keep = qMin(Max_Points, pointError.size()); | 1172 | const int keep = qMin(Max_Points, pointError.size()); |
| 1113 | for (int j=0; j<keep; j++) | 1173 | for (int j=0; j<keep; j++) |
| 1114 | lines.append(QString("Box,%1,%2").arg(QString::number(i), QString::number(pointError[j*(pointError.size()-1)/(keep-1)]))); | 1174 | lines.append(QString("Box,%1,%2").arg(QString::number(i), QString::number(pointError[j*(pointError.size()-1)/(keep-1)]))); |
| 1115 | } | 1175 | } |
| 1116 | 1176 | ||
| 1177 | + const float averagePointError = Common::Mean(averagePointErrors); | ||
| 1178 | + | ||
| 1117 | lines.append(QString("AvgError,0,%1").arg(averagePointError)); | 1179 | lines.append(QString("AvgError,0,%1").arg(averagePointError)); |
| 1180 | + lines.append(QString("NormLength,0,%1").arg(Common::Mean(normalizedLengths))); | ||
| 1118 | 1181 | ||
| 1119 | QtUtils::writeFile(csv, lines); | 1182 | QtUtils::writeFile(csv, lines); |
| 1120 | - qDebug("Average Error: %.3f", averagePointError); | 1183 | + |
| 1184 | + qDebug("Average Error for all Points: %.3f", averagePointError); | ||
| 1185 | + | ||
| 1121 | return averagePointError; | 1186 | return averagePointError; |
| 1122 | } | 1187 | } |
| 1123 | 1188 |
openbr/core/eval.h
| @@ -31,7 +31,7 @@ namespace br | @@ -31,7 +31,7 @@ namespace br | ||
| 31 | 31 | ||
| 32 | void EvalClassification(const QString &predictedGallery, const QString &truthGallery, QString predictedProperty = "", QString truthProperty = ""); | 32 | void EvalClassification(const QString &predictedGallery, const QString &truthGallery, QString predictedProperty = "", QString truthProperty = ""); |
| 33 | float EvalDetection(const QString &predictedGallery, const QString &truthGallery, const QString &csv = "", bool normalize = false, int minSize = 0, int maxSize = 0); // Return average overlap | 33 | float EvalDetection(const QString &predictedGallery, const QString &truthGallery, const QString &csv = "", bool normalize = false, int minSize = 0, int maxSize = 0); // Return average overlap |
| 34 | - float EvalLandmarking(const QString &predictedGallery, const QString &truthGallery, const QString &csv = "", int normalizationIndexA = 0, int normalizationIndexB = 1); // Return average error | 34 | + float EvalLandmarking(const QString &predictedGallery, const QString &truthGallery, const QString &csv = "", int normalizationIndexA = 0, int normalizationIndexB = 1, int sampleIndex = 0, int totalExamples = 5); // Return average error |
| 35 | void EvalRegression(const QString &predictedGallery, const QString &truthGallery, QString predictedProperty = "", QString truthProperty = ""); | 35 | void EvalRegression(const QString &predictedGallery, const QString &truthGallery, QString predictedProperty = "", QString truthProperty = ""); |
| 36 | } | 36 | } |
| 37 | 37 |
openbr/core/plot.cpp
| @@ -482,16 +482,115 @@ bool PlotLandmarking(const QStringList &files, const File &destination, bool sho | @@ -482,16 +482,115 @@ bool PlotLandmarking(const QStringList &files, const File &destination, bool sho | ||
| 482 | qDebug("Plotting %d landmarking file(s) to %s", files.size(), qPrintable(destination)); | 482 | qDebug("Plotting %d landmarking file(s) to %s", files.size(), qPrintable(destination)); |
| 483 | RPlot p(files, destination, false); | 483 | RPlot p(files, destination, false); |
| 484 | 484 | ||
| 485 | - p.file.write("# Split data into individual plots\n" | ||
| 486 | - "plot_index = which(names(data)==\"Plot\")\n" | ||
| 487 | - "Box <- data[grep(\"Box\",data$Plot),-c(1)]\n" | ||
| 488 | - "rm(data)\n" | ||
| 489 | - "\n"); | ||
| 490 | - | ||
| 491 | - p.file.write(qPrintable(QString("ggplot(Box, aes(Y,%1%2))").arg(p.major.size > 1 ? QString(", colour=%1").arg(p.major.header) : QString(), p.minor.size > 1 ? QString(", linetype=%1").arg(p.minor.header) : QString()) + | 485 | + p.file.write(qPrintable(QString("# Split data into individual plots\n" |
| 486 | + "plot_index = which(names(data)==\"Plot\")\n" | ||
| 487 | + "Box <- data[grep(\"Box\",data$Plot),-c(1)]\n" | ||
| 488 | + "Box$X <- factor(Box$X, levels = Box$X, ordered = TRUE)\n" | ||
| 489 | + "Sample <- data[grep(\"Sample\",data$Plot),-c(1)]\n" | ||
| 490 | + "Sample$X <- as.character(Sample$X)\n" | ||
| 491 | + "EXT <- data[grep(\"EXT\",data$Plot),-c(1)]\n" | ||
| 492 | + "EXT$X <- as.character(EXT$X)\n" | ||
| 493 | + "EXP <- data[grep(\"EXP\",data$Plot),-c(1)]\n" | ||
| 494 | + "EXP$X <- as.character(EXP$X)\n" | ||
| 495 | + "NormLength <- data[grep(\"NormLength\",data$Plot),-c(1)]\n" | ||
| 496 | + "rm(data)\n" | ||
| 497 | + "\n"))); | ||
| 498 | + | ||
| 499 | + p.file.write(qPrintable(QString("summarySE <- function(data=NULL, measurevar, groupvars=NULL, na.rm=FALSE, conf.interval=.95, .drop=TRUE) {\n\t" | ||
| 500 | + "require(plyr)\n\n\tlength2 <- function (x, na.rm=FALSE) {\n\t\tif (na.rm) sum(!is.na(x))\n\t\telse length(x)" | ||
| 501 | + "\n\t}\n\n\tdatac <- ddply(data, groupvars, .drop=.drop, .fun = function(xx, col) {\n\t\t" | ||
| 502 | + "c(N=length2(xx[[col]], na.rm=na.rm), mean=mean(xx[[col]], na.rm=na.rm), sd=sd(xx[[col]], na.rm=na.rm))\n\t\t}," | ||
| 503 | + "\n\t\tmeasurevar\n\t)\n\n\tdatac <- rename(datac, c(\"mean\" = measurevar))\n\tdatac$se <- datac$sd / sqrt(datac$N)" | ||
| 504 | + "\n\tciMult <- qt(conf.interval/2 + .5, datac$N-1)\n\tdatac$ci <- datac$se * ciMult\n\n\treturn(datac)\n}\n"))); | ||
| 505 | + | ||
| 506 | + | ||
| 507 | + p.file.write(qPrintable(QString("\nreadData <- function(data) {\n\texamples <- list()\n" | ||
| 508 | + "\tfor (i in 1:nrow(data)) {\n" | ||
| 509 | + "\t\tpath <- data[i,1]\n" | ||
| 510 | + "\t\tvalue <- data[i,2]\n" | ||
| 511 | + "\t\tfile <- unlist(strsplit(path, \"[.]\"))[1]\n" | ||
| 512 | + "\t\text <- unlist(strsplit(path, \"[.]\"))[2]\n" | ||
| 513 | + "\t\tif (ext == \"jpg\" || ext == \"JPEG\" || ext == \"jpeg\" || ext == \"JPG\") {\n" | ||
| 514 | + "\t\t\timg <- readJPEG(path)\n" | ||
| 515 | + "\t\t} else if (ext == \"PNG\" || ext == \"png\") {\n" | ||
| 516 | + "\t\t\timg <- readPNG(path)\n" | ||
| 517 | + "\t\t} else if (ext == \"TIFF\" || ext == \"tiff\" || ext == \"TIF\" || ext == \"tif\") { \n" | ||
| 518 | + "\t\t\timg <- readTIFF(path)\n" | ||
| 519 | + "}else {\n" | ||
| 520 | + "\t\t\tnext\n" | ||
| 521 | + "\t\t}\n" | ||
| 522 | + "\t\texample <- list(file = file, value = value, image = img)\n" | ||
| 523 | + "\t\texamples[[i]] <- example\n" | ||
| 524 | + "\t}\n" | ||
| 525 | + "\treturn(examples)\n" | ||
| 526 | + "}\n"))); | ||
| 527 | + | ||
| 528 | + p.file.write(qPrintable(QString("\nlibrary(jpeg)\n" | ||
| 529 | + "library(png)\n" | ||
| 530 | + "library(grid)\n" | ||
| 531 | + "multiplot <- function(..., plotlist=NULL, cols) {\n" | ||
| 532 | + "\trequire(grid)\n" | ||
| 533 | + "\t# Make a list from the ... arguments and plotlist\n" | ||
| 534 | + "\tplots <- c(list(...), plotlist)\n" | ||
| 535 | + "\tnumPlots = length(plots)\n" | ||
| 536 | + "\t# Make the panel\n" | ||
| 537 | + "\tplotCols = cols\n" | ||
| 538 | + "\tplotRows = ceiling(numPlots/plotCols)\n" | ||
| 539 | + "\t# Set up the page\n" | ||
| 540 | + "\tgrid.newpage()\n" | ||
| 541 | + "\tpushViewport(viewport(layout = grid.layout(plotRows, plotCols)))\n" | ||
| 542 | + "\tvplayout <- function(x, y)\n" | ||
| 543 | + "\tviewport(layout.pos.row = x, layout.pos.col = y)\n" | ||
| 544 | + "\t# Make each plot, in the correct location\n" | ||
| 545 | + "\tfor (i in 1:numPlots) {\n" | ||
| 546 | + "\t\tcurRow = ceiling(i/plotCols)\n" | ||
| 547 | + "\t\tcurCol = (i-1) %% plotCols + 1\n" | ||
| 548 | + "\t\tprint(plots[[i]], vp = vplayout(curRow, curCol))\n" | ||
| 549 | + "\t}\n" | ||
| 550 | + "}\n"))); | ||
| 551 | + | ||
| 552 | + p.file.write(qPrintable(QString("\nplotImage <- function(image, title=NULL, label=NULL) { \n" | ||
| 553 | + "\tp <- qplot(1:10, 1:10, geom=\"blank\") + annotation_custom(rasterGrob(image$image), xmin=-Inf, xmax=Inf, ymin=-Inf, ymax=Inf) + theme(axis.line=element_blank(), axis.title.y=element_blank(), axis.text.x=element_blank(), axis.text.y=element_blank(), line=element_blank(), axis.ticks=element_blank(), panel.background=element_blank()) + labs(title=title) + xlab(label)\n" | ||
| 554 | + "\treturn(p)" | ||
| 555 | + "}\n"))); | ||
| 556 | + | ||
| 557 | + p.file.write(qPrintable(QString("\nsample <- readData(Sample) \n" | ||
| 558 | + "rows <- sample[[1]]$value\n" | ||
| 559 | + "algs <- unique(Box$%1)\n" | ||
| 560 | + "algs <- algs[!duplicated(algs)]\n" | ||
| 561 | + "print(plotImage(sample[[1]],\"Sample Landmarks\",sprintf(\"Total Landmarks: %s\",sample[[1]]$value))) \n" | ||
| 562 | + "if (nrow(EXT) != 0 && nrow(EXP)) {\n" | ||
| 563 | + "\tfor (j in 1:length(algs)) {\n" | ||
| 564 | + "\ttruthSample <- readData(EXT[EXT$. == algs[[j]],])\n" | ||
| 565 | + "\tpredictedSample <- readData(EXP[EXP$. == algs[[j]],])\n" | ||
| 566 | + "\t\tfor (i in 1:length(predictedSample)) {\n" | ||
| 567 | + "\t\t\tmultiplot(plotImage(predictedSample[[i]],sprintf(\"%s\\nPredicted Landmarks\",algs[[j]]),sprintf(\"Average Landmark Error: %.3f\",predictedSample[[i]]$value)),plotImage(truthSample[[i]],\"Ground Truth\\nLandmarks\",\"\"),cols=2)\n" | ||
| 568 | + "\t\t}\n" | ||
| 569 | + "\t}\n" | ||
| 570 | + "}\n").arg(p.major.size > 1 ? p.major.header : (p.minor.header.isEmpty() ? p.major.header : p.minor.header)))); | ||
| 571 | + | ||
| 572 | + p.file.write(qPrintable(QString("\n" | ||
| 573 | + "# Code to format error table\n" | ||
| 574 | + "StatBox <- summarySE(Box, measurevar=\"Y\", groupvars=c(\"%1\",\"X\"))\n" | ||
| 575 | + "OverallStatBox <- summarySE(Box, measurevar=\"Y\", groupvars=c(\"%1\"))\n" | ||
| 576 | + "mat <- matrix(paste(as.character(round(StatBox$Y, 3)), round(StatBox$ci, 3), sep=\" \\u00b1 \"),nrow=rows,ncol=length(algs),byrow=FALSE)\n" | ||
| 577 | + "mat <- rbind(mat, paste(as.character(round(OverallStatBox$Y, 3)), round(OverallStatBox$ci, 3), sep=\" \\u00b1 \"))\n" | ||
| 578 | + "mat <- rbind(mat, as.character(round(NormLength$Y, 3)))\n" | ||
| 579 | + "colnames(mat) <- algs\n" | ||
| 580 | + "rownames(mat) <- c(seq(0,rows-1),\"Aggregate\",\"Average IPD\")\n" | ||
| 581 | + "ETable <- as.table(mat)\n").arg(p.major.size > 1 ? p.major.header : (p.minor.header.isEmpty() ? p.major.header : p.minor.header)))); | ||
| 582 | + | ||
| 583 | + p.file.write(qPrintable(QString("\n" | ||
| 584 | + "print(textplot(ETable))\n" | ||
| 585 | + "print(title(\"Landmarking Error Rates\"))\n"))); | ||
| 586 | + | ||
| 587 | + p.file.write(qPrintable(QString("ggplot(Box, aes(Y,%1%2))").arg(p.major.size > 1 ? QString(", colour=%1").arg(p.major.header) : QString(), | ||
| 588 | + p.minor.size > 1 ? QString(", linetype=%1").arg(p.minor.header) : QString()) + | ||
| 492 | QString(" + annotation_logticks(sides=\"b\") + stat_ecdf() + scale_x_log10(\"Normalized Error\", breaks=c(0.001,0.01,0.1,1,10)) + scale_y_continuous(\"Cumulative Density\", label=percent) + theme_minimal()\n\n"))); | 589 | QString(" + annotation_logticks(sides=\"b\") + stat_ecdf() + scale_x_log10(\"Normalized Error\", breaks=c(0.001,0.01,0.1,1,10)) + scale_y_continuous(\"Cumulative Density\", label=percent) + theme_minimal()\n\n"))); |
| 590 | + | ||
| 493 | p.file.write(qPrintable(QString("ggplot(Box, aes(factor(X), Y%1%2))").arg(p.major.size > 1 ? QString(", colour=%1").arg(p.major.header) : QString(), p.minor.size > 1 ? QString(", linetype=%1").arg(p.minor.header) : QString()) + | 591 | p.file.write(qPrintable(QString("ggplot(Box, aes(factor(X), Y%1%2))").arg(p.major.size > 1 ? QString(", colour=%1").arg(p.major.header) : QString(), p.minor.size > 1 ? QString(", linetype=%1").arg(p.minor.header) : QString()) + |
| 494 | - QString("+ annotation_logticks(sides=\"l\") + geom_boxplot(alpha=0.5) + geom_jitter(size=1, alpha=0.5) + scale_x_discrete(\"Landmark\") + scale_y_log10(\"Normalized Error\", breaks=c(0.01,0.1,1,10)) + theme_minimal()\n\n"))); | 592 | + QString("+ annotation_logticks(sides=\"l\") + geom_boxplot(alpha=0.5) + geom_jitter(size=1, alpha=0.5) + scale_x_discrete(\"Landmark\") + scale_y_log10(\"Normalized Error\", breaks=c(0.001,0.01,0.1,1,10)) + theme_minimal()\n\n"))); |
| 593 | + | ||
| 495 | p.file.write(qPrintable(QString("ggplot(Box, aes(factor(X), Y%1%2))").arg(p.major.size > 1 ? QString(", colour=%1").arg(p.major.header) : QString(), p.minor.size > 1 ? QString(", linetype=%1").arg(p.minor.header) : QString()) + | 594 | p.file.write(qPrintable(QString("ggplot(Box, aes(factor(X), Y%1%2))").arg(p.major.size > 1 ? QString(", colour=%1").arg(p.major.header) : QString(), p.minor.size > 1 ? QString(", linetype=%1").arg(p.minor.header) : QString()) + |
| 496 | QString("+ annotation_logticks(sides=\"l\") + geom_violin(alpha=0.5) + scale_x_discrete(\"Landmark\") + scale_y_log10(\"Normalized Error\", breaks=c(0.001,0.01,0.1,1,10))\n\n"))); | 595 | QString("+ annotation_logticks(sides=\"l\") + geom_violin(alpha=0.5) + scale_x_discrete(\"Landmark\") + scale_y_log10(\"Normalized Error\", breaks=c(0.001,0.01,0.1,1,10))\n\n"))); |
| 497 | 596 |
openbr/core/qtutils.cpp
| @@ -500,6 +500,131 @@ QString getAbsolutePath(const QString &filename) | @@ -500,6 +500,131 @@ QString getAbsolutePath(const QString &filename) | ||
| 500 | return QFileInfo(filename).absoluteFilePath(); | 500 | return QFileInfo(filename).absoluteFilePath(); |
| 501 | } | 501 | } |
| 502 | 502 | ||
| 503 | +BlockCompression::BlockCompression(QIODevice *_basis) | ||
| 504 | +{ | ||
| 505 | + blockSize = 100000000; | ||
| 506 | + setBasis(_basis); | ||
| 507 | +} | ||
| 508 | + | ||
| 509 | +BlockCompression::BlockCompression() { blockSize = 100000000; }; | ||
| 510 | + | ||
| 511 | + | ||
| 512 | +bool BlockCompression::open(QIODevice::OpenMode mode) | ||
| 513 | +{ | ||
| 514 | + this->setOpenMode(mode); | ||
| 515 | + bool res = basis->open(mode); | ||
| 516 | + | ||
| 517 | + if (!res) | ||
| 518 | + return false; | ||
| 519 | + | ||
| 520 | + blockReader.setDevice(basis); | ||
| 521 | + blockWriter.setDevice(basis); | ||
| 522 | + | ||
| 523 | + if (mode & QIODevice::WriteOnly) { | ||
| 524 | + precompressedBlockWriter = new QBuffer; | ||
| 525 | + precompressedBlockWriter->open(QIODevice::ReadWrite); | ||
| 526 | + } | ||
| 527 | + else if (mode & QIODevice::ReadOnly) { | ||
| 528 | + QByteArray compressedBlock; | ||
| 529 | + blockReader >> compressedBlock; | ||
| 530 | + | ||
| 531 | + decompressedBlock = qUncompress(compressedBlock); | ||
| 532 | + decompressedBlockReader.setBuffer(&decompressedBlock); | ||
| 533 | + decompressedBlockReader.open(QIODevice::ReadOnly); | ||
| 534 | + } | ||
| 535 | + | ||
| 536 | + return true; | ||
| 537 | +} | ||
| 538 | + | ||
| 539 | +void BlockCompression::close() | ||
| 540 | +{ | ||
| 541 | + // flush output buffer | ||
| 542 | + if ((openMode() & QIODevice::WriteOnly) && precompressedBlockWriter) { | ||
| 543 | + QByteArray compressedBlock = qCompress(precompressedBlockWriter->buffer(), -1); | ||
| 544 | + blockWriter << compressedBlock; | ||
| 545 | + } | ||
| 546 | + basis->close(); | ||
| 547 | +} | ||
| 548 | + | ||
| 549 | +void BlockCompression::setBasis(QIODevice *_basis) | ||
| 550 | +{ | ||
| 551 | + basis = _basis; | ||
| 552 | + blockReader.setDevice(basis); | ||
| 553 | + blockWriter.setDevice(basis); | ||
| 554 | +} | ||
| 555 | + | ||
| 556 | +// read from current decompressed block, if out of space, read and decompress another | ||
| 557 | +// block from basis | ||
| 558 | +qint64 BlockCompression::readData(char *data, qint64 remaining) | ||
| 559 | +{ | ||
| 560 | + qint64 read = 0; | ||
| 561 | + while (remaining > 0) { | ||
| 562 | + qint64 single_read = decompressedBlockReader.read(data, remaining); | ||
| 563 | + if (single_read == -1) | ||
| 564 | + qFatal("miss read"); | ||
| 565 | + | ||
| 566 | + remaining -= single_read; | ||
| 567 | + read += single_read; | ||
| 568 | + data += single_read; | ||
| 569 | + | ||
| 570 | + // need a new block | ||
| 571 | + if (remaining > 0) { | ||
| 572 | + QByteArray compressedBlock; | ||
| 573 | + blockReader >> compressedBlock; | ||
| 574 | + if (compressedBlock.size() == 0) { | ||
| 575 | + return read; | ||
| 576 | + } | ||
| 577 | + decompressedBlock = qUncompress(compressedBlock); | ||
| 578 | + | ||
| 579 | + decompressedBlockReader.close(); | ||
| 580 | + decompressedBlockReader.setBuffer(&decompressedBlock); | ||
| 581 | + decompressedBlockReader.open(QIODevice::ReadOnly); | ||
| 582 | + } | ||
| 583 | + } | ||
| 584 | + return blockReader.atEnd() && !basis->isReadable() ? -1 : read; | ||
| 585 | +} | ||
| 586 | + | ||
| 587 | +bool BlockCompression::isSequential() const | ||
| 588 | +{ | ||
| 589 | + return true; | ||
| 590 | +} | ||
| 591 | + | ||
| 592 | +qint64 BlockCompression::writeData(const char *data, qint64 remaining) | ||
| 593 | +{ | ||
| 594 | + qint64 written = 0; | ||
| 595 | + | ||
| 596 | + while (remaining > 0) { | ||
| 597 | + // how much more can be put in this buffer? | ||
| 598 | + qint64 capacity = blockSize - precompressedBlockWriter->pos(); | ||
| 599 | + | ||
| 600 | + // don't try to write beyond capacity | ||
| 601 | + qint64 write_size = qMin(capacity, remaining); | ||
| 602 | + | ||
| 603 | + qint64 singleWrite = precompressedBlockWriter->write(data, write_size); | ||
| 604 | + // ignore the error case here, we consdier basis's failure mode the real | ||
| 605 | + // end case | ||
| 606 | + if (singleWrite == -1) | ||
| 607 | + singleWrite = 0; | ||
| 608 | + | ||
| 609 | + remaining -= singleWrite; | ||
| 610 | + data += singleWrite; | ||
| 611 | + written += singleWrite; | ||
| 612 | + | ||
| 613 | + if (remaining > 0) { | ||
| 614 | + QByteArray compressedBlock = qCompress(precompressedBlockWriter->buffer(), -1); | ||
| 615 | + | ||
| 616 | + if (compressedBlock.size() != 0) | ||
| 617 | + blockWriter << compressedBlock; | ||
| 618 | + | ||
| 619 | + delete precompressedBlockWriter; | ||
| 620 | + precompressedBlockWriter = new QBuffer; | ||
| 621 | + precompressedBlockWriter->open(QIODevice::ReadWrite); | ||
| 622 | + } | ||
| 623 | + } | ||
| 624 | + return basis->isWritable() ? written : -1; | ||
| 625 | +} | ||
| 626 | + | ||
| 627 | + | ||
| 503 | 628 | ||
| 504 | } // namespace QtUtils | 629 | } // namespace QtUtils |
| 505 | 630 |
openbr/core/qtutils.h
| @@ -17,6 +17,7 @@ | @@ -17,6 +17,7 @@ | ||
| 17 | #ifndef QTUTILS_QTUTILS_H | 17 | #ifndef QTUTILS_QTUTILS_H |
| 18 | #define QTUTILS_QTUTILS_H | 18 | #define QTUTILS_QTUTILS_H |
| 19 | 19 | ||
| 20 | +#include <QBuffer> | ||
| 20 | #include <QByteArray> | 21 | #include <QByteArray> |
| 21 | #include <QDir> | 22 | #include <QDir> |
| 22 | #include <QFile> | 23 | #include <QFile> |
| @@ -93,6 +94,38 @@ namespace QtUtils | @@ -93,6 +94,38 @@ namespace QtUtils | ||
| 93 | 94 | ||
| 94 | /**** Rect Utilities ****/ | 95 | /**** Rect Utilities ****/ |
| 95 | float overlap(const QRectF &r, const QRectF &s); | 96 | float overlap(const QRectF &r, const QRectF &s); |
| 97 | + | ||
| 98 | + | ||
| 99 | + class BlockCompression : public QIODevice | ||
| 100 | + { | ||
| 101 | + public: | ||
| 102 | + BlockCompression(QIODevice *_basis); | ||
| 103 | + BlockCompression(); | ||
| 104 | + int blockSize; | ||
| 105 | + QIODevice *basis; | ||
| 106 | + | ||
| 107 | + bool open(QIODevice::OpenMode mode); | ||
| 108 | + | ||
| 109 | + void close(); | ||
| 110 | + | ||
| 111 | + void setBasis(QIODevice *_basis); | ||
| 112 | + | ||
| 113 | + QDataStream blockReader; | ||
| 114 | + QByteArray decompressedBlock; | ||
| 115 | + QBuffer decompressedBlockReader; | ||
| 116 | + | ||
| 117 | + // read from current decompressed block, if out of space, read and decompress another | ||
| 118 | + // block from basis | ||
| 119 | + qint64 readData(char *data, qint64 remaining); | ||
| 120 | + | ||
| 121 | + bool isSequential() const; | ||
| 122 | + | ||
| 123 | + // write to a QByteArray, when max block sized is reached, compress and write | ||
| 124 | + // it to basis | ||
| 125 | + QBuffer * precompressedBlockWriter; | ||
| 126 | + QDataStream blockWriter; | ||
| 127 | + qint64 writeData(const char *data, qint64 remaining); | ||
| 128 | + }; | ||
| 96 | } | 129 | } |
| 97 | 130 | ||
| 98 | #endif // QTUTILS_QTUTILS_H | 131 | #endif // QTUTILS_QTUTILS_H |
openbr/openbr.cpp
| @@ -134,9 +134,9 @@ float br_eval_detection(const char *predicted_gallery, const char *truth_gallery | @@ -134,9 +134,9 @@ float br_eval_detection(const char *predicted_gallery, const char *truth_gallery | ||
| 134 | return EvalDetection(predicted_gallery, truth_gallery, csv, normalize, minSize, maxSize); | 134 | return EvalDetection(predicted_gallery, truth_gallery, csv, normalize, minSize, maxSize); |
| 135 | } | 135 | } |
| 136 | 136 | ||
| 137 | -float br_eval_landmarking(const char *predicted_gallery, const char *truth_gallery, const char *csv, int normalization_index_a, int normalization_index_b) | 137 | +float br_eval_landmarking(const char *predicted_gallery, const char *truth_gallery, const char *csv, int normalization_index_a, int normalization_index_b, int sample_index, int total_examples) |
| 138 | { | 138 | { |
| 139 | - return EvalLandmarking(predicted_gallery, truth_gallery, csv, normalization_index_a, normalization_index_b); | 139 | + return EvalLandmarking(predicted_gallery, truth_gallery, csv, normalization_index_a, normalization_index_b, sample_index, total_examples); |
| 140 | } | 140 | } |
| 141 | 141 | ||
| 142 | void br_eval_regression(const char *predicted_gallery, const char *truth_gallery, const char *predicted_property, const char *truth_property) | 142 | void br_eval_regression(const char *predicted_gallery, const char *truth_gallery, const char *predicted_property, const char *truth_property) |
openbr/openbr.h
| @@ -214,8 +214,10 @@ BR_EXPORT float br_eval_detection(const char *predicted_gallery, const char *tru | @@ -214,8 +214,10 @@ BR_EXPORT float br_eval_detection(const char *predicted_gallery, const char *tru | ||
| 214 | * \param csv Optional \c .csv file to contain performance metrics. | 214 | * \param csv Optional \c .csv file to contain performance metrics. |
| 215 | * \param normalization_index_a Optional first index in the list of points to use for normalization. | 215 | * \param normalization_index_a Optional first index in the list of points to use for normalization. |
| 216 | * \param normalization_index_b Optional second index in the list of points to use for normalization. | 216 | * \param normalization_index_b Optional second index in the list of points to use for normalization. |
| 217 | + * \param sample_index Optional index for sample landmark image in ground truth gallery. | ||
| 218 | + * \param total_examples Optional number of accurate and inaccurate examples to display. | ||
| 217 | */ | 219 | */ |
| 218 | -BR_EXPORT float br_eval_landmarking(const char *predicted_gallery, const char *truth_gallery, const char *csv = "", int normalization_index_a = 0, int normalization_index_b = 1); | 220 | +BR_EXPORT float br_eval_landmarking(const char *predicted_gallery, const char *truth_gallery, const char *csv = "", int normalization_index_a = 0, int normalization_index_b = 1, int sample_index = 0, int total_examples = 5); |
| 219 | 221 | ||
| 220 | /*! | 222 | /*! |
| 221 | * \brief Evaluates regression accuracy to disk. | 223 | * \brief Evaluates regression accuracy to disk. |
openbr/plugins/algorithms.cpp
| @@ -31,15 +31,15 @@ class AlgorithmsInitializer : public Initializer | @@ -31,15 +31,15 @@ class AlgorithmsInitializer : public Initializer | ||
| 31 | void initialize() const | 31 | void initialize() const |
| 32 | { | 32 | { |
| 33 | // Face | 33 | // Face |
| 34 | - Globals->abbreviations.insert("FaceRecognition", "FaceDetection+Expand+<FaceRecognitionRegistration>+Expand+<FaceRecognitionExtraction>+<FaceRecognitionEmbedding>+<FaceRecognitionQuantization>+SetMetadata(AlgorithmID,-1):MatchProbability(ByteL1)"); | ||
| 35 | - Globals->abbreviations.insert("GenderClassification", "FaceDetection+Expand+<FaceClassificationRegistration>+Expand+<FaceClassificationExtraction>+<GenderClassifier>+Discard"); | ||
| 36 | - Globals->abbreviations.insert("AgeRegression", "FaceDetection+Expand+<FaceClassificationRegistration>+Expand+<FaceClassificationExtraction>+<AgeRegressor>+Discard"); | 34 | + Globals->abbreviations.insert("FaceRecognition", "FaceDetection+FaceRecognitionRegistration+<FaceRecognitionExtraction>+<FaceRecognitionEmbedding>+<FaceRecognitionQuantization>+SetMetadata(AlgorithmID,-1):Unit(ByteL1)"); |
| 35 | + Globals->abbreviations.insert("GenderClassification", "FaceDetection+Expand+FaceClassificationRegistration+Expand+<FaceClassificationExtraction>+<GenderClassifier>+Discard"); | ||
| 36 | + Globals->abbreviations.insert("AgeRegression", "FaceDetection+Expand+FaceClassificationRegistration+Expand+<FaceClassificationExtraction>+<AgeRegressor>+Discard"); | ||
| 37 | Globals->abbreviations.insert("FaceQuality", "Open+Expand+Cascade(FrontalFace)+ASEFEyes+Affine(64,64,0.25,0.35)+ImageQuality+Cvt(Gray)+DFFS+Discard"); | 37 | Globals->abbreviations.insert("FaceQuality", "Open+Expand+Cascade(FrontalFace)+ASEFEyes+Affine(64,64,0.25,0.35)+ImageQuality+Cvt(Gray)+DFFS+Discard"); |
| 38 | Globals->abbreviations.insert("MedianFace", "Open+Expand+Cascade(FrontalFace)+ASEFEyes+Affine(256,256,0.37,0.45)+Center(Median)"); | 38 | Globals->abbreviations.insert("MedianFace", "Open+Expand+Cascade(FrontalFace)+ASEFEyes+Affine(256,256,0.37,0.45)+Center(Median)"); |
| 39 | Globals->abbreviations.insert("BlurredFaceDetection", "Open+LimitSize(1024)+SkinMask/(Cvt(Gray)+GradientMask)+And+Morph(Erode,16)+LargestConvexArea"); | 39 | Globals->abbreviations.insert("BlurredFaceDetection", "Open+LimitSize(1024)+SkinMask/(Cvt(Gray)+GradientMask)+And+Morph(Erode,16)+LargestConvexArea"); |
| 40 | Globals->abbreviations.insert("DrawFaceDetection", "Open+Cascade(FrontalFace)+Expand+ASEFEyes+Draw(inPlace=true)"); | 40 | Globals->abbreviations.insert("DrawFaceDetection", "Open+Cascade(FrontalFace)+Expand+ASEFEyes+Draw(inPlace=true)"); |
| 41 | Globals->abbreviations.insert("ShowFaceDetection", "DrawFaceDetection+Contract+First+Show+Discard"); | 41 | Globals->abbreviations.insert("ShowFaceDetection", "DrawFaceDetection+Contract+First+Show+Discard"); |
| 42 | - Globals->abbreviations.insert("DownloadFaceRecognition", "Download+Open+ROI+Expand+Cvt(Gray)+Cascade(FrontalFace)+Expand+<FaceRecognitionRegistration>+Expand+<FaceRecognitionExtraction>+<FaceRecognitionEmbedding>+<FaceRecognitionQuantization>+SetMetadata(AlgorithmID,-1):MatchProbability(ByteL1)"); | 42 | + Globals->abbreviations.insert("DownloadFaceRecognition", "Download+Open+ROI+Cvt(Gray)+Cascade(FrontalFace)+FaceRecognitionRegistration+<FaceRecognitionExtraction>+<FaceRecognitionEmbedding>+<FaceRecognitionQuantization>+SetMetadata(AlgorithmID,-1):Unit(ByteL1)"); |
| 43 | Globals->abbreviations.insert("OpenBR", "FaceRecognition"); | 43 | Globals->abbreviations.insert("OpenBR", "FaceRecognition"); |
| 44 | Globals->abbreviations.insert("GenderEstimation", "GenderClassification"); | 44 | Globals->abbreviations.insert("GenderEstimation", "GenderClassification"); |
| 45 | Globals->abbreviations.insert("AgeEstimation", "AgeRegression"); | 45 | Globals->abbreviations.insert("AgeEstimation", "AgeRegression"); |
| @@ -50,7 +50,7 @@ class AlgorithmsInitializer : public Initializer | @@ -50,7 +50,7 @@ class AlgorithmsInitializer : public Initializer | ||
| 50 | // Video | 50 | // Video |
| 51 | Globals->abbreviations.insert("DisplayVideo", "FPSLimit(30)+Show(false,[FrameNumber])+Discard"); | 51 | Globals->abbreviations.insert("DisplayVideo", "FPSLimit(30)+Show(false,[FrameNumber])+Discard"); |
| 52 | Globals->abbreviations.insert("PerFrameDetection", "SaveMat(original)+Cvt(Gray)+Cascade(FrontalFace)+ASEFEyes+RestoreMat(original)+Draw(inPlace=true)+Show(false,[FrameNumber])+Discard"); | 52 | Globals->abbreviations.insert("PerFrameDetection", "SaveMat(original)+Cvt(Gray)+Cascade(FrontalFace)+ASEFEyes+RestoreMat(original)+Draw(inPlace=true)+Show(false,[FrameNumber])+Discard"); |
| 53 | - Globals->abbreviations.insert("AgeGenderDemo", "SaveMat(original)+Cvt(Gray)+Cascade(FrontalFace)+Expand+<FaceClassificationRegistration>+<FaceClassificationExtraction>+<AgeRegressor>/<GenderClassifier>+Discard+RestoreMat(original)+Draw(inPlace=true)+DrawPropertiesPoint([Age,Gender],Affine_0,inPlace=true)+SaveMat(original)+Discard+Contract+RestoreMat(original)+FPSCalc+Show(false,[AvgFPS,Age,Gender])+Discard"); | 53 | + Globals->abbreviations.insert("AgeGenderDemo", "SaveMat(original)+Cvt(Gray)+Cascade(FrontalFace)+Expand+FaceClassificationRegistration+<FaceClassificationExtraction>+<AgeRegressor>/<GenderClassifier>+Discard+RestoreMat(original)+Draw(inPlace=true)+DrawPropertiesPoint([Age,Gender],Affine_0,inPlace=true)+SaveMat(original)+Discard+Contract+RestoreMat(original)+FPSCalc+Show(false,[AvgFPS,Age,Gender])+Discard"); |
| 54 | Globals->abbreviations.insert("ShowOpticalFlowField", "SaveMat(original)+AggregateFrames(2)+OpticalFlow(useMagnitude=false)+Grid(100,100)+DrawOpticalFlow+FPSLimit(30)+Show(false)+Discard"); | 54 | Globals->abbreviations.insert("ShowOpticalFlowField", "SaveMat(original)+AggregateFrames(2)+OpticalFlow(useMagnitude=false)+Grid(100,100)+DrawOpticalFlow+FPSLimit(30)+Show(false)+Discard"); |
| 55 | Globals->abbreviations.insert("ShowOpticalFlowMagnitude", "AggregateFrames(2)+OpticalFlow+Normalize(Range,false,0,255)+Cvt(Color)+Draw+FPSLimit(30)+Show(false)+Discard"); | 55 | Globals->abbreviations.insert("ShowOpticalFlowMagnitude", "AggregateFrames(2)+OpticalFlow+Normalize(Range,false,0,255)+Cvt(Color)+Draw+FPSLimit(30)+Show(false)+Discard"); |
| 56 | Globals->abbreviations.insert("ShowMotionSegmentation", "DropFrames(5)+AggregateFrames(2)+OpticalFlow+CvtUChar+WatershedSegmentation+DrawSegmentation+Draw+FPSLimit(30)+Show(false)+Discard"); | 56 | Globals->abbreviations.insert("ShowMotionSegmentation", "DropFrames(5)+AggregateFrames(2)+OpticalFlow+CvtUChar+WatershedSegmentation+DrawSegmentation+Draw+FPSLimit(30)+Show(false)+Discard"); |
| @@ -92,11 +92,11 @@ class AlgorithmsInitializer : public Initializer | @@ -92,11 +92,11 @@ class AlgorithmsInitializer : public Initializer | ||
| 92 | Globals->abbreviations.insert("DenseHOG", "Gradient+RectRegions(8,8,6,6)+Bin(0,360,8)+Hist(8)"); | 92 | Globals->abbreviations.insert("DenseHOG", "Gradient+RectRegions(8,8,6,6)+Bin(0,360,8)+Hist(8)"); |
| 93 | Globals->abbreviations.insert("DenseSIFT", "(Grid(10,10)+SIFTDescriptor(12)+ByRow)"); | 93 | Globals->abbreviations.insert("DenseSIFT", "(Grid(10,10)+SIFTDescriptor(12)+ByRow)"); |
| 94 | Globals->abbreviations.insert("DenseSIFT2", "(Grid(5,5)+SIFTDescriptor(12)+ByRow)"); | 94 | Globals->abbreviations.insert("DenseSIFT2", "(Grid(5,5)+SIFTDescriptor(12)+ByRow)"); |
| 95 | - Globals->abbreviations.insert("FaceRecognitionRegistration", "(ASEFEyes+Affine(88,88,0.25,0.35)+DownsampleTraining(FTE(DFFS),instances=1))"); | 95 | + Globals->abbreviations.insert("FaceRecognitionRegistration", "ASEFEyes+Affine(88,88,0.25,0.35)"); |
| 96 | Globals->abbreviations.insert("FaceRecognitionExtraction", "(Mask+DenseSIFT/DenseLBP+DownsampleTraining(PCA(0.95),instances=1)+Normalize(L2)+Cat)"); | 96 | Globals->abbreviations.insert("FaceRecognitionExtraction", "(Mask+DenseSIFT/DenseLBP+DownsampleTraining(PCA(0.95),instances=1)+Normalize(L2)+Cat)"); |
| 97 | Globals->abbreviations.insert("FaceRecognitionEmbedding", "(Dup(12)+RndSubspace(0.05,1)+DownsampleTraining(LDA(0.98),instances=-2)+Cat+DownsampleTraining(PCA(768),instances=1))"); | 97 | Globals->abbreviations.insert("FaceRecognitionEmbedding", "(Dup(12)+RndSubspace(0.05,1)+DownsampleTraining(LDA(0.98),instances=-2)+Cat+DownsampleTraining(PCA(768),instances=1))"); |
| 98 | Globals->abbreviations.insert("FaceRecognitionQuantization", "(Normalize(L1)+Quantize)"); | 98 | Globals->abbreviations.insert("FaceRecognitionQuantization", "(Normalize(L1)+Quantize)"); |
| 99 | - Globals->abbreviations.insert("FaceClassificationRegistration", "(ASEFEyes+Affine(56,72,0.33,0.45)+FTE(DFFS))"); | 99 | + Globals->abbreviations.insert("FaceClassificationRegistration", "ASEFEyes+Affine(56,72,0.33,0.45)"); |
| 100 | Globals->abbreviations.insert("FaceClassificationExtraction", "((Grid(7,7)+SIFTDescriptor(8)+ByRow)/DenseLBP+DownsampleTraining(PCA(0.95),instances=-1, inputVariable=Gender)+Cat)"); | 100 | Globals->abbreviations.insert("FaceClassificationExtraction", "((Grid(7,7)+SIFTDescriptor(8)+ByRow)/DenseLBP+DownsampleTraining(PCA(0.95),instances=-1, inputVariable=Gender)+Cat)"); |
| 101 | Globals->abbreviations.insert("AgeRegressor", "DownsampleTraining(Center(Range),instances=-1, inputVariable=Age)+DownsampleTraining(SVM(RBF,EPS_SVR,inputVariable=Age),instances=100, inputVariable=Age)"); | 101 | Globals->abbreviations.insert("AgeRegressor", "DownsampleTraining(Center(Range),instances=-1, inputVariable=Age)+DownsampleTraining(SVM(RBF,EPS_SVR,inputVariable=Age),instances=100, inputVariable=Age)"); |
| 102 | Globals->abbreviations.insert("GenderClassifier", "DownsampleTraining(Center(Range),instances=-1, inputVariable=Gender)+DownsampleTraining(SVM(RBF,C_SVC,inputVariable=Gender),instances=4000, inputVariable=Gender)"); | 102 | Globals->abbreviations.insert("GenderClassifier", "DownsampleTraining(Center(Range),instances=-1, inputVariable=Gender)+DownsampleTraining(SVM(RBF,C_SVC,inputVariable=Gender),instances=4000, inputVariable=Gender)"); |
openbr/plugins/cascade.cpp
| @@ -252,6 +252,8 @@ class CascadeTransform : public MetaTransform | @@ -252,6 +252,8 @@ class CascadeTransform : public MetaTransform | ||
| 252 | void init() | 252 | void init() |
| 253 | { | 253 | { |
| 254 | cascadeResource.setResourceMaker(new CascadeResourceMaker(model)); | 254 | cascadeResource.setResourceMaker(new CascadeResourceMaker(model)); |
| 255 | + if (model == "Ear" || model == "Eye" || model == "FrontalFace" || model == "ProfileFace") | ||
| 256 | + this->trainable = false; | ||
| 255 | } | 257 | } |
| 256 | 258 | ||
| 257 | // Train transform | 259 | // Train transform |
openbr/plugins/draw.cpp
| @@ -42,11 +42,15 @@ class DrawTransform : public UntrainableTransform | @@ -42,11 +42,15 @@ class DrawTransform : public UntrainableTransform | ||
| 42 | Q_PROPERTY(bool rects READ get_rects WRITE set_rects RESET reset_rects STORED false) | 42 | Q_PROPERTY(bool rects READ get_rects WRITE set_rects RESET reset_rects STORED false) |
| 43 | Q_PROPERTY(bool inPlace READ get_inPlace WRITE set_inPlace RESET reset_inPlace STORED false) | 43 | Q_PROPERTY(bool inPlace READ get_inPlace WRITE set_inPlace RESET reset_inPlace STORED false) |
| 44 | Q_PROPERTY(int lineThickness READ get_lineThickness WRITE set_lineThickness RESET reset_lineThickness STORED false) | 44 | Q_PROPERTY(int lineThickness READ get_lineThickness WRITE set_lineThickness RESET reset_lineThickness STORED false) |
| 45 | + Q_PROPERTY(bool named READ get_named WRITE set_named RESET reset_named STORED false) | ||
| 46 | + Q_PROPERTY(bool location READ get_location WRITE set_location RESET reset_location STORED false) | ||
| 45 | BR_PROPERTY(bool, verbose, false) | 47 | BR_PROPERTY(bool, verbose, false) |
| 46 | BR_PROPERTY(bool, points, true) | 48 | BR_PROPERTY(bool, points, true) |
| 47 | BR_PROPERTY(bool, rects, true) | 49 | BR_PROPERTY(bool, rects, true) |
| 48 | BR_PROPERTY(bool, inPlace, false) | 50 | BR_PROPERTY(bool, inPlace, false) |
| 49 | BR_PROPERTY(int, lineThickness, 1) | 51 | BR_PROPERTY(int, lineThickness, 1) |
| 52 | + BR_PROPERTY(bool, named, true) | ||
| 53 | + BR_PROPERTY(bool, location, true) | ||
| 50 | 54 | ||
| 51 | void project(const Template &src, Template &dst) const | 55 | void project(const Template &src, Template &dst) const |
| 52 | { | 56 | { |
| @@ -55,11 +59,12 @@ class DrawTransform : public UntrainableTransform | @@ -55,11 +59,12 @@ class DrawTransform : public UntrainableTransform | ||
| 55 | dst.m() = inPlace ? src.m() : src.m().clone(); | 59 | dst.m() = inPlace ? src.m() : src.m().clone(); |
| 56 | 60 | ||
| 57 | if (points) { | 61 | if (points) { |
| 58 | - const QList<Point2f> pointsList = OpenCVUtils::toPoints(src.file.namedPoints() + src.file.points()); | 62 | + const QList<Point2f> pointsList = (named) ? OpenCVUtils::toPoints(src.file.points()+src.file.namedPoints()) : OpenCVUtils::toPoints(src.file.points()); |
| 59 | for (int i=0; i<pointsList.size(); i++) { | 63 | for (int i=0; i<pointsList.size(); i++) { |
| 60 | const Point2f &point = pointsList[i]; | 64 | const Point2f &point = pointsList[i]; |
| 61 | circle(dst, point, 3, color, -1); | 65 | circle(dst, point, 3, color, -1); |
| 62 | - if (verbose) putText(dst, QString("%1,(%2,%3)").arg(QString::number(i),QString::number(point.x),QString::number(point.y)).toStdString(), point, FONT_HERSHEY_SIMPLEX, 0.5, verboseColor, 1); | 66 | + QString label = (location) ? QString("%1,(%2,%3)").arg(QString::number(i),QString::number(point.x),QString::number(point.y)) : QString("%1").arg(QString::number(i)); |
| 67 | + if (verbose) putText(dst, label.toStdString(), point, FONT_HERSHEY_SIMPLEX, 0.5, verboseColor, 1); | ||
| 63 | } | 68 | } |
| 64 | } | 69 | } |
| 65 | if (rects) { | 70 | if (rects) { |
openbr/plugins/eigen3.cpp
| @@ -600,7 +600,7 @@ class SparseLDATransform : public Transform | @@ -600,7 +600,7 @@ class SparseLDATransform : public Transform | ||
| 600 | 600 | ||
| 601 | //Only works on binary class problems for now | 601 | //Only works on binary class problems for now |
| 602 | assert(ldaOrig.projection.cols() == 1); | 602 | assert(ldaOrig.projection.cols() == 1); |
| 603 | - float ldaStd = eigStd(ldaOrig.projection); | 603 | + float ldaStd = EigenUtils::stddev(ldaOrig.projection); |
| 604 | for (int i = 0; i < ldaOrig.projection.rows(); i++) | 604 | for (int i = 0; i < ldaOrig.projection.rows(); i++) |
| 605 | if (abs(ldaOrig.projection(i)) > varThreshold * ldaStd) | 605 | if (abs(ldaOrig.projection(i)) > varThreshold * ldaStd) |
| 606 | selections.append(i); | 606 | selections.append(i); |
openbr/plugins/filter.cpp
| @@ -61,11 +61,23 @@ class BlurTransform : public UntrainableTransform | @@ -61,11 +61,23 @@ class BlurTransform : public UntrainableTransform | ||
| 61 | { | 61 | { |
| 62 | Q_OBJECT | 62 | Q_OBJECT |
| 63 | Q_PROPERTY(float sigma READ get_sigma WRITE set_sigma RESET reset_sigma STORED false) | 63 | Q_PROPERTY(float sigma READ get_sigma WRITE set_sigma RESET reset_sigma STORED false) |
| 64 | + Q_PROPERTY(bool ROI READ get_ROI WRITE set_ROI RESET reset_ROI STORED false) | ||
| 64 | BR_PROPERTY(float, sigma, 1) | 65 | BR_PROPERTY(float, sigma, 1) |
| 66 | + BR_PROPERTY(bool, ROI, false) | ||
| 65 | 67 | ||
| 66 | void project(const Template &src, Template &dst) const | 68 | void project(const Template &src, Template &dst) const |
| 67 | { | 69 | { |
| 68 | - GaussianBlur(src, dst, Size(0,0), sigma); | 70 | + if (!ROI) GaussianBlur(src, dst, Size(0,0), sigma); |
| 71 | + else { | ||
| 72 | + dst.m() = src.m(); | ||
| 73 | + foreach (const QRectF &rect, src.file.rects()) { | ||
| 74 | + Rect region(rect.x(), rect.y(), rect.width(), rect.height()); | ||
| 75 | + Mat input = dst.m(); | ||
| 76 | + Mat output = input.clone(); | ||
| 77 | + GaussianBlur(input(region), output(region), Size(0,0), sigma); | ||
| 78 | + dst.m() = output; | ||
| 79 | + } | ||
| 80 | + } | ||
| 69 | } | 81 | } |
| 70 | }; | 82 | }; |
| 71 | 83 |
openbr/plugins/gui.cpp
| @@ -764,7 +764,8 @@ public: | @@ -764,7 +764,8 @@ public: | ||
| 764 | } | 764 | } |
| 765 | else | 765 | else |
| 766 | { | 766 | { |
| 767 | - dst[i].file.set(labelSet[idx], rectSet[idx]); | 767 | + if (labels.isEmpty()) dst[i].file.appendRect(rectSet[idx]); |
| 768 | + else dst[i].file.set(labelSet[idx], rectSet[idx]); | ||
| 768 | } | 769 | } |
| 769 | } | 770 | } |
| 770 | } | 771 | } |
openbr/plugins/meta.cpp
| @@ -17,6 +17,8 @@ | @@ -17,6 +17,8 @@ | ||
| 17 | #include <QFutureSynchronizer> | 17 | #include <QFutureSynchronizer> |
| 18 | #include <QRegularExpression> | 18 | #include <QRegularExpression> |
| 19 | #include <QtConcurrentRun> | 19 | #include <QtConcurrentRun> |
| 20 | +#include <qbuffer.h> | ||
| 21 | + | ||
| 20 | #include "openbr_internal.h" | 22 | #include "openbr_internal.h" |
| 21 | #include "openbr/core/common.h" | 23 | #include "openbr/core/common.h" |
| 22 | #include "openbr/core/opencvutils.h" | 24 | #include "openbr/core/opencvutils.h" |
| @@ -94,17 +96,15 @@ class PipeTransform : public CompositeTransform | @@ -94,17 +96,15 @@ class PipeTransform : public CompositeTransform | ||
| 94 | 96 | ||
| 95 | int i = 0; | 97 | int i = 0; |
| 96 | while (i < transforms.size()) { | 98 | while (i < transforms.size()) { |
| 97 | - fprintf(stderr, "\n%s", qPrintable(transforms[i]->objectName())); | ||
| 98 | - | ||
| 99 | // Conditional statement covers likely case that first transform is untrainable | 99 | // Conditional statement covers likely case that first transform is untrainable |
| 100 | if (transforms[i]->trainable) { | 100 | if (transforms[i]->trainable) { |
| 101 | - fprintf(stderr, " training..."); | 101 | + qDebug() << "Training " << transforms[i]->description() << "\n..."; |
| 102 | transforms[i]->train(dataLines); | 102 | transforms[i]->train(dataLines); |
| 103 | } | 103 | } |
| 104 | 104 | ||
| 105 | // if the transform is time varying, we can't project it in parallel | 105 | // if the transform is time varying, we can't project it in parallel |
| 106 | if (transforms[i]->timeVarying()) { | 106 | if (transforms[i]->timeVarying()) { |
| 107 | - fprintf(stderr, "\n%s projecting...", qPrintable(transforms[i]->objectName())); | 107 | + qDebug() << "Projecting " << transforms[i]->description() << "\n..."; |
| 108 | for (int j=0; j < dataLines.size();j++) { | 108 | for (int j=0; j < dataLines.size();j++) { |
| 109 | TemplateList junk; | 109 | TemplateList junk; |
| 110 | splitFTEs(dataLines[j], junk); | 110 | splitFTEs(dataLines[j], junk); |
| @@ -130,7 +130,16 @@ class PipeTransform : public CompositeTransform | @@ -130,7 +130,16 @@ class PipeTransform : public CompositeTransform | ||
| 130 | !transforms[nextTrainableTransform]->timeVarying()) | 130 | !transforms[nextTrainableTransform]->timeVarying()) |
| 131 | nextTrainableTransform++; | 131 | nextTrainableTransform++; |
| 132 | 132 | ||
| 133 | - fprintf(stderr, " projecting..."); | 133 | + // No more trainable transforms? Don't need any more projects then |
| 134 | + if (nextTrainableTransform == transforms.size()) | ||
| 135 | + break; | ||
| 136 | + | ||
| 137 | + fprintf(stderr, "Projecting %s", qPrintable(transforms[i]->description())); | ||
| 138 | + for (int j=i+1; j < nextTrainableTransform; j++) | ||
| 139 | + fprintf(stderr,"+%s", qPrintable(transforms[j]->description())); | ||
| 140 | + fprintf(stderr, "\n...\n"); | ||
| 141 | + fflush(stderr); | ||
| 142 | + | ||
| 134 | QFutureSynchronizer<void> futures; | 143 | QFutureSynchronizer<void> futures; |
| 135 | for (int j=0; j < dataLines.size(); j++) | 144 | for (int j=0; j < dataLines.size(); j++) |
| 136 | futures.addFuture(QtConcurrent::run(this, &PipeTransform::_projectPartial, &dataLines[j], i, nextTrainableTransform)); | 145 | futures.addFuture(QtConcurrent::run(this, &PipeTransform::_projectPartial, &dataLines[j], i, nextTrainableTransform)); |
| @@ -510,7 +519,6 @@ class LoadStoreTransform : public MetaTransform | @@ -510,7 +519,6 @@ class LoadStoreTransform : public MetaTransform | ||
| 510 | 519 | ||
| 511 | public: | 520 | public: |
| 512 | Transform *transform; | 521 | Transform *transform; |
| 513 | - QString baseName; | ||
| 514 | 522 | ||
| 515 | LoadStoreTransform() : transform(NULL) {} | 523 | LoadStoreTransform() : transform(NULL) {} |
| 516 | 524 | ||
| @@ -540,8 +548,8 @@ private: | @@ -540,8 +548,8 @@ private: | ||
| 540 | void init() | 548 | void init() |
| 541 | { | 549 | { |
| 542 | if (transform != NULL) return; | 550 | if (transform != NULL) return; |
| 543 | - if (fileName.isEmpty()) baseName = QRegExp("^[_a-zA-Z0-9]+$").exactMatch(transformString) ? transformString : QtUtils::shortTextHash(transformString); | ||
| 544 | - else baseName = fileName; | 551 | + if (fileName.isEmpty()) fileName = QRegExp("^[_a-zA-Z0-9]+$").exactMatch(transformString) ? transformString : QtUtils::shortTextHash(transformString); |
| 552 | + | ||
| 545 | if (!tryLoad()) | 553 | if (!tryLoad()) |
| 546 | transform = make(transformString); | 554 | transform = make(transformString); |
| 547 | else | 555 | else |
| @@ -553,19 +561,28 @@ private: | @@ -553,19 +561,28 @@ private: | ||
| 553 | return transform->timeVarying(); | 561 | return transform->timeVarying(); |
| 554 | } | 562 | } |
| 555 | 563 | ||
| 556 | - void train(const TemplateList &data) | 564 | + void train(const QList<TemplateList> &data) |
| 557 | { | 565 | { |
| 558 | if (QFileInfo(getFileName()).exists()) | 566 | if (QFileInfo(getFileName()).exists()) |
| 559 | return; | 567 | return; |
| 560 | 568 | ||
| 561 | transform->train(data); | 569 | transform->train(data); |
| 562 | 570 | ||
| 563 | - qDebug("Storing %s", qPrintable(baseName)); | ||
| 564 | - QByteArray byteArray; | ||
| 565 | - QDataStream stream(&byteArray, QFile::WriteOnly); | ||
| 566 | - stream << transform->description(); | 571 | + qDebug("Storing %s", qPrintable(fileName)); |
| 572 | + QtUtils::BlockCompression compressedOut; | ||
| 573 | + QFile fout(fileName); | ||
| 574 | + QtUtils::touchDir(fout); | ||
| 575 | + compressedOut.setBasis(&fout); | ||
| 576 | + | ||
| 577 | + QDataStream stream(&compressedOut); | ||
| 578 | + QString desc = transform->description(); | ||
| 579 | + | ||
| 580 | + if (!compressedOut.open(QFile::WriteOnly)) | ||
| 581 | + qFatal("Failed to open %s for writing.", qPrintable(file)); | ||
| 582 | + | ||
| 583 | + stream << desc; | ||
| 567 | transform->store(stream); | 584 | transform->store(stream); |
| 568 | - QtUtils::writeFile(baseName, byteArray, -1); | 585 | + compressedOut.close(); |
| 569 | } | 586 | } |
| 570 | 587 | ||
| 571 | void project(const Template &src, Template &dst) const | 588 | void project(const Template &src, Template &dst) const |
| @@ -595,8 +612,8 @@ private: | @@ -595,8 +612,8 @@ private: | ||
| 595 | 612 | ||
| 596 | QString getFileName() const | 613 | QString getFileName() const |
| 597 | { | 614 | { |
| 598 | - if (QFileInfo(baseName).exists()) return baseName; | ||
| 599 | - const QString file = Globals->sdkPath + "/share/openbr/models/transforms/" + baseName; | 615 | + if (QFileInfo(fileName).exists()) return fileName; |
| 616 | + const QString file = Globals->sdkPath + "/share/openbr/models/transforms/" + fileName; | ||
| 600 | return QFileInfo(file).exists() ? file : QString(); | 617 | return QFileInfo(file).exists() ? file : QString(); |
| 601 | } | 618 | } |
| 602 | 619 | ||
| @@ -606,12 +623,19 @@ private: | @@ -606,12 +623,19 @@ private: | ||
| 606 | if (file.isEmpty()) return false; | 623 | if (file.isEmpty()) return false; |
| 607 | 624 | ||
| 608 | qDebug("Loading %s", qPrintable(file)); | 625 | qDebug("Loading %s", qPrintable(file)); |
| 609 | - QByteArray data; | ||
| 610 | - QtUtils::readFile(file, data, true); | ||
| 611 | - QDataStream stream(&data, QFile::ReadOnly); | 626 | + QFile fin(file); |
| 627 | + QtUtils::BlockCompression reader(&fin); | ||
| 628 | + if (!reader.open(QIODevice::ReadOnly)) { | ||
| 629 | + if (QFileInfo(file).exists()) qFatal("Unable to open %s for reading. Check file permissions.", qPrintable(file)); | ||
| 630 | + else qFatal("Unable to open %s for reading. File does not exist.", qPrintable(file)); | ||
| 631 | + } | ||
| 632 | + | ||
| 633 | + QDataStream stream(&reader); | ||
| 612 | stream >> transformString; | 634 | stream >> transformString; |
| 635 | + | ||
| 613 | transform = Transform::make(transformString); | 636 | transform = Transform::make(transformString); |
| 614 | transform->load(stream); | 637 | transform->load(stream); |
| 638 | + | ||
| 615 | return true; | 639 | return true; |
| 616 | } | 640 | } |
| 617 | }; | 641 | }; |
openbr/plugins/misc.cpp
| @@ -115,6 +115,9 @@ private: | @@ -115,6 +115,9 @@ private: | ||
| 115 | void project(const Template &src, Template &dst) const | 115 | void project(const Template &src, Template &dst) const |
| 116 | { | 116 | { |
| 117 | dst.file = src.file; | 117 | dst.file = src.file; |
| 118 | + if (Globals->verbose) | ||
| 119 | + qDebug("Opening %s", qPrintable(src.file.flat())); | ||
| 120 | + | ||
| 118 | if (src.empty()) { | 121 | if (src.empty()) { |
| 119 | const Mat img = imread(src.file.resolved().toStdString(), mode); | 122 | const Mat img = imread(src.file.resolved().toStdString(), mode); |
| 120 | if (img.data) dst.append(img); | 123 | if (img.data) dst.append(img); |
| @@ -126,6 +129,8 @@ private: | @@ -126,6 +129,8 @@ private: | ||
| 126 | else dst.file.fte = true; | 129 | else dst.file.fte = true; |
| 127 | } | 130 | } |
| 128 | } | 131 | } |
| 132 | + if (dst.file.fte) | ||
| 133 | + qWarning("Error opening %s", qPrintable(src.file.flat())); | ||
| 129 | } | 134 | } |
| 130 | }; | 135 | }; |
| 131 | BR_REGISTER(Transform, ReadTransform) | 136 | BR_REGISTER(Transform, ReadTransform) |
openbr/plugins/process.cpp
| @@ -349,6 +349,7 @@ public: | @@ -349,6 +349,7 @@ public: | ||
| 349 | QByteArray data = readArray; | 349 | QByteArray data = readArray; |
| 350 | QDataStream deserializer(data); | 350 | QDataStream deserializer(data); |
| 351 | Transform *res = Transform::deserialize(deserializer); | 351 | Transform *res = Transform::deserialize(deserializer); |
| 352 | + sendSignal(CommunicationManager::OUTPUT_AVAILABLE); | ||
| 352 | return res; | 353 | return res; |
| 353 | } | 354 | } |
| 354 | 355 | ||
| @@ -534,6 +535,8 @@ struct ProcessData | @@ -534,6 +535,8 @@ struct ProcessData | ||
| 534 | class ProcessWrapperTransform : public WrapperTransform | 535 | class ProcessWrapperTransform : public WrapperTransform |
| 535 | { | 536 | { |
| 536 | Q_OBJECT | 537 | Q_OBJECT |
| 538 | + Q_PROPERTY(int concurrentCount READ get_concurrentCount WRITE set_concurrentCount RESET reset_concurrentCount STORED false) | ||
| 539 | + BR_PROPERTY(int, concurrentCount, 2) | ||
| 537 | 540 | ||
| 538 | QString baseKey; | 541 | QString baseKey; |
| 539 | 542 | ||
| @@ -579,17 +582,33 @@ class ProcessWrapperTransform : public WrapperTransform | @@ -579,17 +582,33 @@ class ProcessWrapperTransform : public WrapperTransform | ||
| 579 | if (transform) { | 582 | if (transform) { |
| 580 | QDataStream out(&serialized, QFile::WriteOnly); | 583 | QDataStream out(&serialized, QFile::WriteOnly); |
| 581 | transform->serialize(out); | 584 | transform->serialize(out); |
| 585 | + tcount = Globals->parallelism; | ||
| 586 | + counter.acquire(counter.available()); | ||
| 587 | + counter.release(this->concurrentCount); | ||
| 582 | } | 588 | } |
| 583 | } | 589 | } |
| 584 | 590 | ||
| 585 | - QByteArray serialized; | 591 | + static QSemaphore counter; |
| 592 | + mutable int tcount; | ||
| 593 | + mutable QByteArray serialized; | ||
| 586 | void transmitTForm(CommunicationManager *localComm) const | 594 | void transmitTForm(CommunicationManager *localComm) const |
| 587 | { | 595 | { |
| 588 | if (serialized.isEmpty() ) | 596 | if (serialized.isEmpty() ) |
| 589 | qFatal("Trying to transmit empty transform!"); | 597 | qFatal("Trying to transmit empty transform!"); |
| 590 | 598 | ||
| 599 | + counter.acquire(1); | ||
| 600 | + static QMutex transmission; | ||
| 601 | + QMutexLocker lock(&transmission); | ||
| 602 | + tcount--; | ||
| 603 | + | ||
| 591 | localComm->writeArray = serialized; | 604 | localComm->writeArray = serialized; |
| 605 | + if (tcount == 0) | ||
| 606 | + serialized.clear(); | ||
| 607 | + lock.unlock(); | ||
| 608 | + | ||
| 592 | emit localComm->pulseSendSerialized(); | 609 | emit localComm->pulseSendSerialized(); |
| 610 | + localComm->getSignal(); | ||
| 611 | + counter.release(1); | ||
| 593 | } | 612 | } |
| 594 | 613 | ||
| 595 | void activateProcess(ProcessData *data) const | 614 | void activateProcess(ProcessData *data) const |
| @@ -633,6 +652,7 @@ public: | @@ -633,6 +652,7 @@ public: | ||
| 633 | bool processActive; | 652 | bool processActive; |
| 634 | ProcessWrapperTransform() : WrapperTransform(false) { processActive = false; } | 653 | ProcessWrapperTransform() : WrapperTransform(false) { processActive = false; } |
| 635 | }; | 654 | }; |
| 655 | +QSemaphore ProcessWrapperTransform::counter; | ||
| 636 | 656 | ||
| 637 | BR_REGISTER(Transform, ProcessWrapperTransform) | 657 | BR_REGISTER(Transform, ProcessWrapperTransform) |
| 638 | 658 |
openbr/plugins/quality.cpp
| @@ -77,6 +77,12 @@ class ImpostorUniquenessMeasureTransform : public Transform | @@ -77,6 +77,12 @@ class ImpostorUniquenessMeasureTransform : public Transform | ||
| 77 | 77 | ||
| 78 | BR_REGISTER(Transform, ImpostorUniquenessMeasureTransform) | 78 | BR_REGISTER(Transform, ImpostorUniquenessMeasureTransform) |
| 79 | 79 | ||
| 80 | + | ||
| 81 | +float KDEPointer(const QList<float> *scores, double x, double h) | ||
| 82 | +{ | ||
| 83 | + return Common::KernelDensityEstimation(*scores, x, h); | ||
| 84 | +} | ||
| 85 | + | ||
| 80 | /* Kernel Density Estimator */ | 86 | /* Kernel Density Estimator */ |
| 81 | struct KDE | 87 | struct KDE |
| 82 | { | 88 | { |
| @@ -85,20 +91,35 @@ struct KDE | @@ -85,20 +91,35 @@ struct KDE | ||
| 85 | QList<float> bins; | 91 | QList<float> bins; |
| 86 | 92 | ||
| 87 | KDE() : min(0), max(1), mean(0), stddev(1) {} | 93 | KDE() : min(0), max(1), mean(0), stddev(1) {} |
| 88 | - KDE(const QList<float> &scores) | 94 | + |
| 95 | + KDE(const QList<float> &scores, bool trainKDE) | ||
| 89 | { | 96 | { |
| 90 | Common::MinMax(scores, &min, &max); | 97 | Common::MinMax(scores, &min, &max); |
| 91 | Common::MeanStdDev(scores, &mean, &stddev); | 98 | Common::MeanStdDev(scores, &mean, &stddev); |
| 99 | + | ||
| 100 | + if (!trainKDE) | ||
| 101 | + return; | ||
| 102 | + | ||
| 92 | double h = Common::KernelDensityBandwidth(scores); | 103 | double h = Common::KernelDensityBandwidth(scores); |
| 93 | const int size = 255; | 104 | const int size = 255; |
| 94 | bins.reserve(size); | 105 | bins.reserve(size); |
| 95 | - for (int i=0; i<size; i++) | ||
| 96 | - bins.append(Common::KernelDensityEstimation(scores, min + (max-min)*i/(size-1), h)); | 106 | + |
| 107 | + QFutureSynchronizer<float> futures; | ||
| 108 | + | ||
| 109 | + for (int i=0; i < size; i++) | ||
| 110 | + futures.addFuture(QtConcurrent::run(KDEPointer, &scores, min + (max-min)*i/(size-1), h)); | ||
| 111 | + futures.waitForFinished(); | ||
| 112 | + | ||
| 113 | + foreach(const QFuture<float> & future, futures.futures()) | ||
| 114 | + bins.append(future.result()); | ||
| 97 | } | 115 | } |
| 98 | 116 | ||
| 99 | float operator()(float score, bool gaussian = true) const | 117 | float operator()(float score, bool gaussian = true) const |
| 100 | { | 118 | { |
| 101 | if (gaussian) return 1/(stddev*sqrt(2*CV_PI))*exp(-0.5*pow((score-mean)/stddev, 2)); | 119 | if (gaussian) return 1/(stddev*sqrt(2*CV_PI))*exp(-0.5*pow((score-mean)/stddev, 2)); |
| 120 | + if (bins.empty()) | ||
| 121 | + return -std::numeric_limits<float>::max(); | ||
| 122 | + | ||
| 102 | if (score <= min) return bins.first(); | 123 | if (score <= min) return bins.first(); |
| 103 | if (score >= max) return bins.last(); | 124 | if (score >= max) return bins.last(); |
| 104 | const float x = (score-min)/(max-min)*bins.size(); | 125 | const float x = (score-min)/(max-min)*bins.size(); |
| @@ -123,8 +144,8 @@ struct MP | @@ -123,8 +144,8 @@ struct MP | ||
| 123 | { | 144 | { |
| 124 | KDE genuine, impostor; | 145 | KDE genuine, impostor; |
| 125 | MP() {} | 146 | MP() {} |
| 126 | - MP(const QList<float> &genuineScores, const QList<float> &impostorScores) | ||
| 127 | - : genuine(genuineScores), impostor(impostorScores) {} | 147 | + MP(const QList<float> &genuineScores, const QList<float> &impostorScores, bool trainKDE) |
| 148 | + : genuine(genuineScores, trainKDE), impostor(impostorScores, trainKDE) {} | ||
| 128 | float operator()(float score, bool gaussian = true) const | 149 | float operator()(float score, bool gaussian = true) const |
| 129 | { | 150 | { |
| 130 | const float g = genuine(score, gaussian); | 151 | const float g = genuine(score, gaussian); |
| @@ -165,7 +186,7 @@ class MatchProbabilityDistance : public Distance | @@ -165,7 +186,7 @@ class MatchProbabilityDistance : public Distance | ||
| 165 | const QList<int> labels = src.indexProperty(inputVariable); | 186 | const QList<int> labels = src.indexProperty(inputVariable); |
| 166 | QScopedPointer<MatrixOutput> matrixOutput(MatrixOutput::make(FileList(src.size()), FileList(src.size()))); | 187 | QScopedPointer<MatrixOutput> matrixOutput(MatrixOutput::make(FileList(src.size()), FileList(src.size()))); |
| 167 | distance->compare(src, src, matrixOutput.data()); | 188 | distance->compare(src, src, matrixOutput.data()); |
| 168 | - | 189 | + |
| 169 | QList<float> genuineScores, impostorScores; | 190 | QList<float> genuineScores, impostorScores; |
| 170 | genuineScores.reserve(labels.size()); | 191 | genuineScores.reserve(labels.size()); |
| 171 | impostorScores.reserve(labels.size()*labels.size()); | 192 | impostorScores.reserve(labels.size()*labels.size()); |
| @@ -178,8 +199,8 @@ class MatchProbabilityDistance : public Distance | @@ -178,8 +199,8 @@ class MatchProbabilityDistance : public Distance | ||
| 178 | else impostorScores.append(score); | 199 | else impostorScores.append(score); |
| 179 | } | 200 | } |
| 180 | } | 201 | } |
| 181 | - | ||
| 182 | - mp = MP(genuineScores, impostorScores); | 202 | + |
| 203 | + mp = MP(genuineScores, impostorScores, !gaussian); | ||
| 183 | } | 204 | } |
| 184 | 205 | ||
| 185 | float compare(const Template &target, const Template &query) const | 206 | float compare(const Template &target, const Template &query) const |
openbr/plugins/stasm4.cpp
| @@ -180,7 +180,7 @@ private: | @@ -180,7 +180,7 @@ private: | ||
| 180 | void project(const Template &src, Template &dst) const | 180 | void project(const Template &src, Template &dst) const |
| 181 | { | 181 | { |
| 182 | QList<float> paramList = src.file.getList<float>("affineParameters"); | 182 | QList<float> paramList = src.file.getList<float>("affineParameters"); |
| 183 | - Eigen::MatrixXf points = pointsToMatrix(src.file.points(), true); | 183 | + Eigen::MatrixXf points = EigenUtils::pointsToMatrix(src.file.points(), true); |
| 184 | Eigen::MatrixXf affine = Eigen::MatrixXf::Zero(3, 3); | 184 | Eigen::MatrixXf affine = Eigen::MatrixXf::Zero(3, 3); |
| 185 | for (int i = 0, cnt = 0; i < 2; i++) | 185 | for (int i = 0, cnt = 0; i < 2; i++) |
| 186 | for (int j = 0; j < 3; j++, cnt++) | 186 | for (int j = 0; j < 3; j++, cnt++) |
| @@ -192,7 +192,7 @@ private: | @@ -192,7 +192,7 @@ private: | ||
| 192 | points = affineInv * pointsT; | 192 | points = affineInv * pointsT; |
| 193 | dst = src; | 193 | dst = src; |
| 194 | dst.file.clearPoints(); | 194 | dst.file.clearPoints(); |
| 195 | - dst.file.setPoints(matrixToPoints(points.transpose())); | 195 | + dst.file.setPoints(EigenUtils::matrixToPoints(points.transpose())); |
| 196 | } | 196 | } |
| 197 | }; | 197 | }; |
| 198 | 198 |