diff --git a/app/examples/age_estimation.cpp b/app/examples/age_estimation.cpp index 592241b..c2a90e5 100644 --- a/app/examples/age_estimation.cpp +++ b/app/examples/age_estimation.cpp @@ -29,7 +29,7 @@ static void printTemplate(const br::Template &t) { - printf("%s age: %d\n", qPrintable(t.file.fileName()), int(t.file.label())); + printf("%s age: %d\n", qPrintable(t.file.fileName()), int(t.file.get("Subject"))); } int main(int argc, char *argv[]) diff --git a/app/examples/gender_estimation.cpp b/app/examples/gender_estimation.cpp index 6013c3a..dd4ebd2 100644 --- a/app/examples/gender_estimation.cpp +++ b/app/examples/gender_estimation.cpp @@ -29,7 +29,7 @@ static void printTemplate(const br::Template &t) { - printf("%s gender: %s\n", qPrintable(t.file.fileName()), t.file.label() == 1 ? "Female" : "Male"); + printf("%s gender: %s\n", qPrintable(t.file.fileName()), qPrintable(t.file.get("Subject"))); } int main(int argc, char *argv[]) diff --git a/openbr/core/bee.cpp b/openbr/core/bee.cpp index a513b74..fb840ca 100644 --- a/openbr/core/bee.cpp +++ b/openbr/core/bee.cpp @@ -96,7 +96,7 @@ void BEE::writeSigset(const QString &sigset, const br::FileList &files, bool ign if ((key == "Index") || (key == "Subject")) continue; metadata.append(key+"=\""+QtUtils::toString(file.value(key))+"\""); } - lines.append("\t"); + lines.append("\t("Subject") +"\">"); lines.append("\t\t"); lines.append("\t"); } @@ -260,26 +260,28 @@ void BEE::makeMask(const QString &targetInput, const QString &queryInput, const cv::Mat BEE::makeMask(const br::FileList &targets, const br::FileList &queries, int partition) { - QList targetLabels = targets.labels(); - QList queryLabels = queries.labels(); + // Would like to use indexProperty for this, but didn't make a version of that for Filelist yet + // -cao + QList targetLabels = targets.get("Subject", "-1"); + QList queryLabels = queries.get("Subject", "-1"); QList targetPartitions = targets.crossValidationPartitions(); QList queryPartitions = queries.crossValidationPartitions(); Mat mask(queries.size(), targets.size(), CV_8UC1); for (int i=0; i("Subject"); + QString trueSubject = truth[i].file.get("Subject"); QStringList predictedSubjects(predictedSubject); QStringList trueSubjects(trueSubject); @@ -66,11 +66,12 @@ void br::EvalClassification(const QString &predictedInput, const QString &truthI counters[subject].falsePositive += 1.f / predictedSubjects.size(); } - QSharedPointer output(Output::make("", FileList() << "Subject" << "Count" << "Precision" << "Recall" << "F-score", FileList(counters.size()))); + const QStringList keys = counters.keys(); + QSharedPointer output(Output::make("", FileList() << "Count" << "Precision" << "Recall" << "F-score", FileList(keys))); int tpc = 0; int fnc = 0; - const QStringList keys = counters.keys(); + for (int i=0; isetRelative(File("", subject).label(), i, 0); - output->setRelative(count, i, 1); - output->setRelative(precision, i, 2); - output->setRelative(recall, i, 3); - output->setRelative(fscore, i, 4); + output->setRelative(count, i, 0); + output->setRelative(precision, i, 1); + output->setRelative(recall, i, 2); + output->setRelative(fscore, i, 3); } qDebug("Overall Accuracy = %f", (float)tpc / (float)(tpc + fnc)); @@ -103,9 +103,9 @@ void br::EvalRegression(const QString &predictedInput, const QString &truthInput for (int i=0; i("Subject")-truth[i].file.get("Subject"), 2.f); + truthValues.append(QString::number(truth[i].file.get("Subject"))); + predictedValues.append(QString::number(predicted[i].file.get("Subject"))); } QStringList rSource; diff --git a/openbr/core/cluster.cpp b/openbr/core/cluster.cpp index 79e5f7e..fdff3d8 100644 --- a/openbr/core/cluster.cpp +++ b/openbr/core/cluster.cpp @@ -278,7 +278,9 @@ void br::EvalClustering(const QString &csv, const QString &input) { qDebug("Evaluating %s against %s", qPrintable(csv), qPrintable(input)); - QList labels = TemplateList::fromGallery(input).files().labels(); + // We assume clustering algorithms store assigned cluster labels as integers (since the clusters are + // not named). + QList labels = TemplateList::fromGallery(input).files().get("Subject"); QHash labelToIndex; int nClusters = 0; diff --git a/openbr/core/core.cpp b/openbr/core/core.cpp index d24317c..5f97d70 100644 --- a/openbr/core/core.cpp +++ b/openbr/core/core.cpp @@ -78,7 +78,6 @@ struct AlgorithmCore const bool hasComparer = !distance.isNull(); out << hasComparer; if (hasComparer) distance->store(out); - out << Globals->subjects; // Compress and save to file QtUtils::writeFile(model, data, -1); @@ -98,7 +97,6 @@ struct AlgorithmCore transform->load(in); bool hasDistance; in >> hasDistance; if (hasDistance) distance->load(in); - in >> Globals->subjects; } File getMemoryGallery(const File &file) const diff --git a/openbr/core/opencvutils.cpp b/openbr/core/opencvutils.cpp index fb5622c..48601e9 100644 --- a/openbr/core/opencvutils.cpp +++ b/openbr/core/opencvutils.cpp @@ -119,6 +119,17 @@ Mat OpenCVUtils::toMat(const QList &src, int rows) return dst; } +Mat OpenCVUtils::toMat(const QList &src, int rows) +{ + if (rows == -1) rows = src.size(); + int columns = src.isEmpty() ? 0 : src.size() / rows; + if (rows*columns != src.size()) qFatal("Invalid matrix size."); + Mat dst(rows, columns, CV_32FC1); + for (int i=0; i(i/columns,i%columns) = src[i]; + return dst; +} + Mat OpenCVUtils::toMat(const QList &src) { if (src.isEmpty()) return Mat(); diff --git a/openbr/core/opencvutils.h b/openbr/core/opencvutils.h index ecf6067..0e49fa0 100644 --- a/openbr/core/opencvutils.h +++ b/openbr/core/opencvutils.h @@ -35,6 +35,8 @@ namespace OpenCVUtils // To image cv::Mat toMat(const QList &src, int rows = -1); + cv::Mat toMat(const QList &src, int rows = -1); + cv::Mat toMat(const QList &src); // Data organized one matrix per row cv::Mat toMatByRow(const QList &src); // Data organized one row per row diff --git a/openbr/frvt2012.cpp b/openbr/frvt2012.cpp index d574755..ac47ac8 100644 --- a/openbr/frvt2012.cpp +++ b/openbr/frvt2012.cpp @@ -132,7 +132,7 @@ int32_t SdkEstimator::estimate_age(const ONEFACE &input_face, int32_t &age) TemplateList templates; templates.append(templateFromONEFACE(input_face)); templates >> *frvt2012_age_transform.data(); - age = templates.first().file.label(); + age = templates.first().file.get("Subject"); return templates.first().file.failed() ? 4 : 0; } @@ -141,6 +141,6 @@ int32_t SdkEstimator::estimate_gender(const ONEFACE &input_face, int8_t &gender, TemplateList templates; templates.append(templateFromONEFACE(input_face)); templates >> *frvt2012_gender_transform.data(); - mf = gender = templates.first().file.label(); + mf = gender = templates.first().file.get("Subject") == "Male" ? 0 : 1; return templates.first().file.failed() ? 4 : 0; } diff --git a/openbr/gui/classifier.cpp b/openbr/gui/classifier.cpp index d93b818..2df9f5e 100644 --- a/openbr/gui/classifier.cpp +++ b/openbr/gui/classifier.cpp @@ -44,13 +44,13 @@ void Classifier::_classify(File file) if (algorithm == "GenderClassification") { key = "Gender"; - value = (f.label() == 0 ? "Male" : "Female"); + value = (f.get("Subject")); } else if (algorithm == "AgeRegression") { key = "Age"; - value = QString::number(int(f.label()+0.5)) + " Years"; + value = QString::number(int(f.get("Subject")+0.5)) + " Years"; } else { key = algorithm; - value = QString::number(f.label()); + value = f.get("Subject"); } break; } diff --git a/openbr/openbr_plugin.cpp b/openbr/openbr_plugin.cpp index ff25764..68b9066 100644 --- a/openbr/openbr_plugin.cpp +++ b/openbr/openbr_plugin.cpp @@ -167,34 +167,6 @@ bool File::getBool(const QString &key, bool defaultValue) const return variant.value(); } -QString File::subject() const -{ - const QVariant l = m_metadata.value("Label"); - if (!l.isNull()) return Globals->subjects.key(l.toFloat(), l.toString()); - return m_metadata.value("Subject").toString(); -} - -float File::label() const -{ - const QVariant l = m_metadata.value("Label"); - if (!l.isNull()) return l.toFloat(); - - const QVariant s = m_metadata.value("Subject"); - if (s.isNull()) return -1; - - const QString subject = s.toString(); - - bool is_num = false; - float num = subject.toFloat(&is_num); - if (is_num) return num; - - static QMutex mutex; - QMutexLocker mutexLocker(&mutex); - if (!Globals->subjects.contains(subject)) - Globals->subjects.insert(subject, Globals->subjects.size()); - return Globals->subjects.value(subject); -} - QList File::namedPoints() const { QList landmarks; @@ -360,14 +332,6 @@ void FileList::sort(const QString& key) *this = sortedList; } -QList FileList::labels() const -{ - QList labels; labels.reserve(size()); - foreach (const File &f, *this) - labels.append(f.label()); - return labels; -} - QList FileList::crossValidationPartitions() const { QList crossValidationPartitions; crossValidationPartitions.reserve(size()); @@ -449,7 +413,7 @@ TemplateList TemplateList::fromGallery(const br::File &gallery) newTemplates[i].file.set("Partition", -1); } else { - const QByteArray md5 = QCryptographicHash::hash(newTemplates[i].file.subject().toLatin1(), QCryptographicHash::Md5); + const QByteArray md5 = QCryptographicHash::hash(newTemplates[i].file.get("Subject").toLatin1(), QCryptographicHash::Md5); // Select the right 8 hex characters so that it can be represented as a 64 bit integer without overflow newTemplates[i].file.set("Partition", md5.toHex().right(8).toULongLong(0, 16) % crossValidate); } @@ -469,11 +433,13 @@ TemplateList TemplateList::fromGallery(const br::File &gallery) return templates; } -TemplateList TemplateList::relabel(const TemplateList &tl) +// indexes some property, assigns an integer id to each unique value of propName +// stores the index values in "Label" of the output template list +TemplateList TemplateList::relabel(const TemplateList &tl, const QString & propName) { - const QList originalLabels = tl.labels(); - QHash labelTable; - foreach (int label, originalLabels) + const QList originalLabels = tl.get(propName); + QHash labelTable; + foreach (const QString & label, originalLabels) if (!labelTable.contains(label)) labelTable.insert(label, labelTable.size()); @@ -483,6 +449,52 @@ TemplateList TemplateList::relabel(const TemplateList &tl) return result; } +QList TemplateList::indexProperty(const QString & propName, QHash * valueMap,QHash * reverseLookup) const +{ + QHash dummyForwards; + QHash dummyBackwards; + + if (!valueMap) valueMap = &dummyForwards; + if (!reverseLookup) reverseLookup = &dummyBackwards; + + return indexProperty(propName, *valueMap, *reverseLookup); +} + +QList TemplateList::indexProperty(const QString & propName, QHash & valueMap, QHash & reverseLookup) const +{ + valueMap.clear(); + reverseLookup.clear(); + + const QList originalLabels = values(propName); + foreach (const QVariant & label, originalLabels) { + QString labelString = label.toString(); + if (!valueMap.contains(labelString)) { + reverseLookup.insert(valueMap.size(), label); + valueMap.insert(labelString, valueMap.size()); + } + } + + QList result; + for (int i=0; i TemplateList::applyIndex(const QString & propName, const QHash & valueMap) const +{ + const QList originalLabels = get(propName); + + QList result; + for (int i=0; i(row,column); - return Globals->subjects.key(label, QString::number(label)); - } return QString::number(data.at(row,column)); } diff --git a/openbr/openbr_plugin.h b/openbr/openbr_plugin.h index 81611b5..dc8a438 100644 --- a/openbr/openbr_plugin.h +++ b/openbr/openbr_plugin.h @@ -254,8 +254,6 @@ struct BR_EXPORT File return variant.value(); } - QString subject() const; /*!< \brief Looks up the subject from the file's label. */ - float label() const; /*!< \brief Convenience function for retrieving the file's \c Label. */ inline bool failed() const { return getBool("FTE") || getBool("FTO"); } /*!< \brief Returns \c true if the file failed to open or enroll, \c false otherwise. */ QList namedPoints() const; /*!< \brief Returns points convertible from metadata keys. */ @@ -299,7 +297,24 @@ struct BR_EXPORT FileList : public QList QStringList flat() const; /*!< \brief Returns br::File::flat() for each file in the list. */ QStringList names() const; /*!< \brief Returns #br::File::name for each file in the list. */ void sort(const QString& key); /*!< \brief Sort the list based on metadata. */ - QList labels() const; /*!< \brief Returns br::File::label() for each file in the list. */ + /*!< \brief Returns values associated with the input propName for each file in the list. */ + template + QList get(const QString & propName) const + { + QList values; values.reserve(size()); + foreach (const File &f, *this) + values.append(f.get(propName)); + return values; + } + template + QList get(const QString & propName, T defaultValue) const + { + QList values; values.reserve(size()); + foreach (const File &f, *this) + values.append(f.contains(propName) ? f.get(propName) : defaultValue); + return values; + } + QList crossValidationPartitions() const; /*!< \brief Returns the cross-validation partition (default=0) for each file in the list. */ int failures() const; /*!< \brief Returns the number of files with br::File::failed(). */ }; @@ -383,7 +398,14 @@ struct TemplateList : public QList