diff --git a/openbr/core/common.h b/openbr/core/common.h index 09c171e..056dbaa 100644 --- a/openbr/core/common.h +++ b/openbr/core/common.h @@ -116,15 +116,24 @@ T Max(const QList &vals) } /*! + * \brief Returns the sum of a vector of values. + */ +template class V, typename T> +double Sum(const V &vals) +{ + double sum = 0; + foreach (T val, vals) sum += val; + return sum; +} + +/*! * \brief Returns the mean and standard deviation of a vector of values. */ template class V, typename T> double Mean(const V &vals) { if (vals.isEmpty()) return 0; - double sum = 0; - foreach (T val, vals) sum += val; - return sum / vals.size(); + return Sum(vals) / vals.size(); } /*! diff --git a/openbr/plugins/output.cpp b/openbr/plugins/output.cpp index b58bf49..df4fb13 100644 --- a/openbr/plugins/output.cpp +++ b/openbr/plugins/output.cpp @@ -365,15 +365,22 @@ BR_REGISTER(Output, EmptyOutput) class evalOutput : public MatrixOutput { Q_OBJECT + Q_PROPERTY(QString target READ get_target WRITE set_target RESET reset_target STORED false) + Q_PROPERTY(QString query READ get_query WRITE set_query RESET reset_query STORED false) Q_PROPERTY(bool crossValidate READ get_crossValidate WRITE set_crossValidate RESET reset_crossValidate STORED false) BR_PROPERTY(bool, crossValidate, true) + BR_PROPERTY(QString, target, QString()) + BR_PROPERTY(QString, query, QString()) ~evalOutput() { + if (!target.isEmpty()) targetFiles = TemplateList::fromGallery(target).files(); + if (!query.isEmpty()) queryFiles = TemplateList::fromGallery(query).files(); + if (data.data) { const QString csv = QString(file.name).replace(".eval", ".csv"); if ((Globals->crossValidate == 0) || (!crossValidate)) { - Evaluate(data,targetFiles, queryFiles, csv); + Evaluate(data, targetFiles, queryFiles, csv); } else { QFutureSynchronizer futures; for (int i=0; icrossValidate; i++) diff --git a/openbr/plugins/turk.cpp b/openbr/plugins/turk.cpp index 89f48f1..18f6f48 100644 --- a/openbr/plugins/turk.cpp +++ b/openbr/plugins/turk.cpp @@ -1,4 +1,5 @@ #include "openbr_internal.h" +#include "openbr/core/common.h" #include "openbr/core/qtutils.h" namespace br @@ -13,41 +14,61 @@ class turkGallery : public Gallery { Q_OBJECT - TemplateList readBlock(bool *done) + struct Attribute : public QStringList { - *done = true; - TemplateList templates; - if (!file.exists()) return templates; + QString name; + Attribute(const QString &str = QString()) + { + const int i = str.indexOf('['); + name = str.mid(0, i); + if (i != -1) + append(str.mid(i+1, str.length()-i-2).split(",")); + } - QStringList lines = QtUtils::readLines(file); - QRegExp regexp(",(?!(?:\\w+,?)+\\])"); + Attribute normalized() const + { + bool ok; + QList values; + foreach (const QString &value, *this) { + values.append(value.toFloat(&ok)); + if (!ok) + qFatal("Can't normalize non-numeric vector!"); + } - QStringList headers; + Attribute normal(name); + float sum = Common::Sum(values); + if (sum == 0) sum = 1; + for (int i=0; i headers; + if (!lines.isEmpty()) + foreach (const QString &header, parse(lines.takeFirst())) + headers.append(header); + TemplateList templates; foreach (const QString &line, lines) { - QStringList words = line.split(regexp); - if (words.size() != headers.size()) continue; + QStringList words = parse(line); + if (words.size() != headers.size()) + qFatal("turkGallery invalid column count"); + File f; f.name = words[0]; f.set("Label", words[0].mid(0,5)); for (int i=1; i categoryMap; - for (int j=0; j map = t.file.get >(variable); - QMapIterator i(map); - bool ok; - - while (i.hasNext()) { - i.next(); - // Normalize to [minRange,maxRange] - float value = i.value().toFloat(&ok)*(maxRange-minRange)/maxVotes - minRange; - if (!ok) qFatal("Failed to expand Turk votes for %s", qPrintable(variable)); - if (classify) (value > maxRange-((maxRange-minRange)/2)) ? value = maxRange : value = minRange; - else if (consensusOnly && (value != maxRange && value != minRange)) continue; - expandedT.file.set(i.key(),value); - } - - return expandedT; -} - -/*! - * \ingroup transforms - * \brief Converts Amazon MTurk labels to a non-map format for use in a transform - * \author Scott Klum \cite sklum - */ -class TurkTransform : public UntrainableTransform -{ - Q_OBJECT - Q_PROPERTY(QString HIT READ get_HIT WRITE set_HIT RESET reset_HIT STORED false) - Q_PROPERTY(float maxVotes READ get_maxVotes WRITE set_maxVotes RESET reset_maxVotes STORED false) - Q_PROPERTY(float maxRange READ get_maxRange WRITE set_maxRange RESET reset_maxRange STORED false) - Q_PROPERTY(float minRange READ get_minRange WRITE set_minRange RESET reset_minRange STORED false) - Q_PROPERTY(bool classify READ get_classify WRITE set_classify RESET reset_classify STORED false) - Q_PROPERTY(bool consensusOnly READ get_consensusOnly WRITE set_consensusOnly RESET reset_consensusOnly STORED false) - BR_PROPERTY(QString, HIT, QString()) - BR_PROPERTY(float, maxVotes, 1) - BR_PROPERTY(float, maxRange, 1) - BR_PROPERTY(float, minRange, 0) - BR_PROPERTY(bool, classify, false) - BR_PROPERTY(bool, consensusOnly, false) - - void project(const Template &src, Template &dst) const - { - dst = unmap(src, HIT, maxVotes, maxRange, minRange, classify, consensusOnly); - } -}; - -BR_REGISTER(Transform, TurkTransform) - /*! * \ingroup transforms * \brief Convenience class for training turk attribute regressors @@ -124,23 +94,17 @@ class TurkClassifierTransform : public Transform Q_OBJECT Q_PROPERTY(QString key READ get_key WRITE set_key RESET reset_key STORED false) Q_PROPERTY(QStringList values READ get_values WRITE set_values RESET reset_values STORED false) - Q_PROPERTY(float maxVotes READ get_maxVotes WRITE set_maxVotes RESET reset_maxVotes STORED false) BR_PROPERTY(QString, key, QString()) BR_PROPERTY(QStringList, values, QStringList()) - BR_PROPERTY(float, maxVotes, 1) Transform *child; void init() { - QString algorithm = QString("Turk(%1, %2)+").arg(key, QString::number(maxVotes)); QStringList classifiers; foreach (const QString &value, values) - classifiers.append(QString("SVM(RBF,EPS_SVR,returnDFVal=true,inputVariable=%1,outputVariable=predicted_%1)").arg(value)); - algorithm += classifiers.join("/"); - if (values.size() > 1) - algorithm += "+Cat"; - child = Transform::make(algorithm); + classifiers.append(QString("SVM(RBF,EPS_SVR,returnDFVal=true,inputVariable=%1,outputVariable=predicted_%1)").arg(key + "_" + value)); + child = Transform::make(classifiers.join("/") + (classifiers.size() > 1 ? "+Cat" : "")); } void train(const QList &data) @@ -167,44 +131,6 @@ class TurkClassifierTransform : public Transform BR_REGISTER(Transform, TurkClassifierTransform) /*! - * \ingroup transforms - * \brief Converts metadata into a map structure - * \author Scott Klum \cite sklum - */ -class MapTransform : public UntrainableTransform -{ - Q_OBJECT - Q_PROPERTY(QStringList inputVariables READ get_inputVariables WRITE set_inputVariables RESET reset_inputVariables STORED false) - Q_PROPERTY(QString outputVariable READ get_outputVariable WRITE set_outputVariable RESET reset_outputVariable STORED false) - BR_PROPERTY(QStringList, inputVariables, QStringList()) - BR_PROPERTY(QString, outputVariable, QString()) - - void project(const Template &src, Template &dst) const - { - dst = map(src); - } - - Template map(const Template &t) const { - Template mappedT = t; - QMap map; - - foreach(const QString &s, inputVariables) { - if (t.file.contains(s)) { - map.insert(s,t.file.value(s)); - mappedT.file.remove(s); - } - } - - if (!map.isEmpty()) mappedT.file.set(outputVariable,map); - - return mappedT; - } -}; - -BR_REGISTER(Transform, MapTransform) - - -/*! * \ingroup distances * \brief Unmaps Turk HITs to be compared against query mats * \author Scott Klum \cite sklum @@ -212,33 +138,17 @@ BR_REGISTER(Transform, MapTransform) class TurkDistance : public Distance { Q_OBJECT - Q_PROPERTY(QString HIT READ get_HIT WRITE set_HIT RESET reset_HIT) - Q_PROPERTY(QStringList keys READ get_keys WRITE set_keys RESET reset_keys STORED false) - Q_PROPERTY(float maxVotes READ get_maxVotes WRITE set_maxVotes RESET reset_maxVotes STORED false) - Q_PROPERTY(float maxRange READ get_maxRange WRITE set_maxRange RESET reset_maxRange STORED false) - Q_PROPERTY(float minRange READ get_minRange WRITE set_minRange RESET reset_minRange STORED false) - Q_PROPERTY(bool classify READ get_classify WRITE set_classify RESET reset_classify STORED false) - Q_PROPERTY(bool consensusOnly READ get_consensusOnly WRITE set_consensusOnly RESET reset_consensusOnly STORED false) - BR_PROPERTY(QString, HIT, QString()) - BR_PROPERTY(QStringList, keys, QStringList()) - BR_PROPERTY(float, maxVotes, 1) - BR_PROPERTY(float, maxRange, 1) - BR_PROPERTY(float, minRange, 0) - BR_PROPERTY(bool, classify, false) - BR_PROPERTY(bool, consensusOnly, false) + Q_PROPERTY(QString key READ get_key WRITE set_key RESET reset_key) + Q_PROPERTY(QStringList values READ get_values WRITE set_values RESET reset_values STORED false) + BR_PROPERTY(QString, key, QString()) + BR_PROPERTY(QStringList, values, QStringList()) float compare(const Template &target, const Template &query) const { - Template t = unmap(target, HIT, maxVotes, maxRange, minRange, classify, consensusOnly); - - QList targetValues; - foreach(const QString &s, keys) targetValues.append(t.file.get(s)); - - float stddev = .75; - + const float stddev = .75; float score = 0; - for (int i=0; i(0,i)-targetValues[i])/stddev, 2)); - + for (int i=0; i(0,i)-target.file.get(key + "_" + values[i]))/stddev, 2)); return score; } };