Commit f2e360a1c24fc1d14e2a5b559ab7b83afb85157e

Authored by Scott Klum
2 parents fac59bd2 b290d3f4

Merge

CHANGELOG.md
@@ -4,6 +4,8 @@ @@ -4,6 +4,8 @@
4 * Enrolling files/folders are now sorted naturally instead of alpha numerically 4 * Enrolling files/folders are now sorted naturally instead of alpha numerically
5 * YouTubeFacesDBTransform implements Dr. Wolf's experimental protocol 5 * YouTubeFacesDBTransform implements Dr. Wolf's experimental protocol
6 * NEC3 refactored 6 * NEC3 refactored
  7 +* Updated transform API to add support for time-varying transforms per issue (#23)
  8 +* Refactored File class to improve point and rect storage (#22)
7 9
8 0.2.0 - 2/23/13 10 0.2.0 - 2/23/13
9 =============== 11 ===============
app/br/CMakeLists.txt
@@ -5,3 +5,6 @@ endif() @@ -5,3 +5,6 @@ endif()
5 add_executable(br br.cpp ${BR_RESOURCES}) 5 add_executable(br br.cpp ${BR_RESOURCES})
6 target_link_libraries(br openbr ${CMAKE_THREAD_LIBS_INIT}) 6 target_link_libraries(br openbr ${CMAKE_THREAD_LIBS_INIT})
7 install(TARGETS br RUNTIME DESTINATION bin) 7 install(TARGETS br RUNTIME DESTINATION bin)
  8 +
  9 +add_test(NAME br_initialize WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND br)
  10 +add_test(NAME br_objects WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND br -objects)
app/examples/age_estimation.cpp
@@ -31,7 +31,7 @@ static void printTemplate(const br::Template &t) @@ -31,7 +31,7 @@ static void printTemplate(const br::Template &t)
31 { 31 {
32 printf("%s age: %d\n", 32 printf("%s age: %d\n",
33 qPrintable(t.file.fileName()), 33 qPrintable(t.file.fileName()),
34 - t.file.getInt("Label")); 34 + t.file.get<int>("Label"));
35 } 35 }
36 36
37 int main(int argc, char *argv[]) 37 int main(int argc, char *argv[])
app/examples/face_recognition.cpp
@@ -30,10 +30,9 @@ @@ -30,10 +30,9 @@
30 30
31 static void printTemplate(const br::Template &t) 31 static void printTemplate(const br::Template &t)
32 { 32 {
33 - printf("%s eyes: (%d, %d) (%d, %d)\n",  
34 - qPrintable(t.file.fileName()),  
35 - t.file.getInt("Affine_0_X"), t.file.getInt("Affine_0_Y"),  
36 - t.file.getInt("Affine_1_X"), t.file.getInt("Affine_1_Y")); 33 + const QPoint firstEye = t.file.get<QPoint>("Affine_0");
  34 + const QPoint secondEye = t.file.get<QPoint>("Affine_1");
  35 + printf("%s eyes: (%d, %d) (%d, %d)\n", qPrintable(t.file.fileName()), firstEye.x(), firstEye.y(), secondEye.x(), secondEye.y());
37 } 36 }
38 37
39 int main(int argc, char *argv[]) 38 int main(int argc, char *argv[])
app/examples/face_recognition_evaluation.cpp
@@ -43,9 +43,9 @@ int main(int argc, char *argv[]) @@ -43,9 +43,9 @@ int main(int argc, char *argv[])
43 // Equivalent to 'Globals->path = "../data/MEDS/img/";' in C++ API 43 // Equivalent to 'Globals->path = "../data/MEDS/img/";' in C++ API
44 br_set_property("path", "../data/MEDS/img/"); 44 br_set_property("path", "../data/MEDS/img/");
45 45
46 - // Enroll galleries  
47 - br_enroll("../data/MEDS/sigset/MEDS_frontal_target.xml", "target.gal");  
48 - br_enroll("../data/MEDS/sigset/MEDS_frontal_query.xml", "query.gal"); 46 + // Enroll galleries, don't re-enroll if they already exist (cache)
  47 + br_enroll("../data/MEDS/sigset/MEDS_frontal_target.xml", "target.gal[cache]");
  48 + br_enroll("../data/MEDS/sigset/MEDS_frontal_query.xml", "query.gal[cache]");
49 49
50 // Compare galleries and store result in a binary similarity matrix 50 // Compare galleries and store result in a binary similarity matrix
51 br_compare("target.gal", "query.gal", "FaceRecognition_MEDS.mtx"); 51 br_compare("target.gal", "query.gal", "FaceRecognition_MEDS.mtx");
app/examples/face_recognition_search.cpp 0 → 100644
  1 +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  2 + * Copyright 2012 The MITRE Corporation *
  3 + * *
  4 + * Licensed under the Apache License, Version 2.0 (the "License"); *
  5 + * you may not use this file except in compliance with the License. *
  6 + * You may obtain a copy of the License at *
  7 + * *
  8 + * http://www.apache.org/licenses/LICENSE-2.0 *
  9 + * *
  10 + * Unless required by applicable law or agreed to in writing, software *
  11 + * distributed under the License is distributed on an "AS IS" BASIS, *
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
  13 + * See the License for the specific language governing permissions and *
  14 + * limitations under the License. *
  15 + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  16 +
  17 +/*!
  18 + * \ingroup cli
  19 + * \page cli_face_recognition_search Face Recognition Search
  20 + * \ref cpp_face_recognition_search "C++ Equivalent"
  21 + * \code
  22 + * $ br -algorithm FaceRecognition -enrollAll -enroll ../data/MEDS/img 'meds.gal;meds.csv[separator=;]'
  23 + * $ br -algorithm FaceRecognition -compare meds.gal ../data/MEDS/img/S001-01-t10_01.jpg match_scores.csv
  24 + * \endcode
  25 + */
  26 +
  27 +//! [face_recognition_search]
  28 +#include <openbr_plugin.h>
  29 +
  30 +int main(int argc, char *argv[])
  31 +{
  32 + br::Context::initialize(argc, argv);
  33 +
  34 + // Retrieve classes for enrolling and comparing templates using the FaceRecognition algorithm
  35 + QSharedPointer<br::Transform> transform = br::Transform::fromAlgorithm("FaceRecognition");
  36 + QSharedPointer<br::Distance> distance = br::Distance::fromAlgorithm("FaceRecognition");
  37 +
  38 + // Initialize templates
  39 + br::TemplateList target = br::TemplateList::fromGallery("../data/MEDS/img");
  40 + br::Template query("../data/MEDS/img/S001-01-t10_01.jpg");
  41 +
  42 + // Enroll templates
  43 + br::Globals->enrollAll = true; // Enroll 0 or more faces per image
  44 + target >> *transform;
  45 + br::Globals->enrollAll = false; // Enroll exactly one face per image
  46 + query >> *transform;
  47 +
  48 + // Compare templates
  49 + QList<float> scores = distance->compare(target, query);
  50 +
  51 + // Print an example score
  52 + printf("Images %s and %s have a match score of %.3f\n",
  53 + qPrintable(target[3].file.name),
  54 + qPrintable(query.file.name),
  55 + scores[3]);
  56 +
  57 + br::Context::finalize();
  58 + return 0;
  59 +}
  60 +//! [face_recognition_search]
app/examples/gender_estimation.cpp
@@ -31,7 +31,7 @@ static void printTemplate(const br::Template &amp;t) @@ -31,7 +31,7 @@ static void printTemplate(const br::Template &amp;t)
31 { 31 {
32 printf("%s gender: %s\n", 32 printf("%s gender: %s\n",
33 qPrintable(t.file.fileName()), 33 qPrintable(t.file.fileName()),
34 - t.file.getInt("Label") == 1 ? "Female" : "Male"); 34 + t.file.get<int>("Label") == 1 ? "Female" : "Male");
35 } 35 }
36 36
37 int main(int argc, char *argv[]) 37 int main(int argc, char *argv[])
app/openbr-gui/classifier.cpp
@@ -39,19 +39,18 @@ void Classifier::_classify(File file) @@ -39,19 +39,18 @@ void Classifier::_classify(File file)
39 { 39 {
40 QString key, value; 40 QString key, value;
41 foreach (const File &f, Enroll(file.flat(), File("[algorithm=" + algorithm + "]"))) { 41 foreach (const File &f, Enroll(file.flat(), File("[algorithm=" + algorithm + "]"))) {
42 - qDebug() << f.flat();  
43 if (!f.contains("Label")) 42 if (!f.contains("Label"))
44 continue; 43 continue;
45 44
46 if (algorithm == "GenderClassification") { 45 if (algorithm == "GenderClassification") {
47 key = "Gender"; 46 key = "Gender";
48 - value = (f.getInt("Label", 0) == 0 ? "Male" : "Female"); 47 + value = (f.get<int>("Label", 0) == 0 ? "Male" : "Female");
49 } else if (algorithm == "AgeRegression") { 48 } else if (algorithm == "AgeRegression") {
50 key = "Age"; 49 key = "Age";
51 - value = QString::number(int(f.getFloat("Label", 0)+0.5)) + " Years"; 50 + value = QString::number(int(f.get<float>("Label", 0)+0.5)) + " Years";
52 } else { 51 } else {
53 key = algorithm; 52 key = algorithm;
54 - value = f.getString("Label"); 53 + value = f.get<QString>("Label");
55 } 54 }
56 break; 55 break;
57 } 56 }
app/openbr-gui/gallerytoolbar.cpp
@@ -117,7 +117,7 @@ void br::GalleryToolBar::checkWebcam() @@ -117,7 +117,7 @@ void br::GalleryToolBar::checkWebcam()
117 void br::GalleryToolBar::enrollmentFinished() 117 void br::GalleryToolBar::enrollmentFinished()
118 { 118 {
119 if (files.isEmpty()) { 119 if (files.isEmpty()) {
120 - if (input.getBool("enrollAll") && !tbWebcam.isChecked()) { 120 + if (input.get<bool>("enrollAll", false) && !tbWebcam.isChecked()) {
121 QMessageBox msgBox; 121 QMessageBox msgBox;
122 msgBox.setText("Quality test failed."); 122 msgBox.setText("Quality test failed.");
123 msgBox.setInformativeText("Enroll anyway?"); 123 msgBox.setInformativeText("Enroll anyway?");
@@ -127,7 +127,7 @@ void br::GalleryToolBar::enrollmentFinished() @@ -127,7 +127,7 @@ void br::GalleryToolBar::enrollmentFinished()
127 127
128 if (ret == QMessageBox::Ok) { 128 if (ret == QMessageBox::Ok) {
129 br::File file = input; 129 br::File file = input;
130 - file.setBool("enrollAll", false); 130 + file.set("enrollAll", false);
131 enroll(file); 131 enroll(file);
132 } 132 }
133 } 133 }
app/openbr-gui/templatemetadata.cpp
@@ -28,7 +28,7 @@ void br::TemplateMetadata::setFile(const br::File &amp;file) @@ -28,7 +28,7 @@ void br::TemplateMetadata::setFile(const br::File &amp;file)
28 { 28 {
29 if (file.isNull()) lFile.clear(); 29 if (file.isNull()) lFile.clear();
30 else lFile.setText("<b>File:</b> " + file.fileName()); 30 else lFile.setText("<b>File:</b> " + file.fileName());
31 - lQuality.setText(QString("<b>Quality:</b> %1").arg(file.getBool("FTE") ? "Low" : "High")); 31 + lQuality.setText(QString("<b>Quality:</b> %1").arg(file.get<bool>("FTE", false) ? "Low" : "High"));
32 foreach (const ConditionalClassifier &classifier, conditionalClassifiers) 32 foreach (const ConditionalClassifier &classifier, conditionalClassifiers)
33 if (classifier.action->isVisible()) classifier.classifier->classify(file); 33 if (classifier.action->isVisible()) classifier.classifier->classify(file);
34 } 34 }
app/openbr-gui/templateviewer.cpp
@@ -39,10 +39,8 @@ void TemplateViewer::setFile(const File &amp;file_) @@ -39,10 +39,8 @@ void TemplateViewer::setFile(const File &amp;file_)
39 39
40 // Update landmarks 40 // Update landmarks
41 landmarks.clear(); 41 landmarks.clear();
42 - if (file.contains("Affine_0_X") && file.contains("Affine_0_Y"))  
43 - landmarks.append(QPointF(file.getFloat("Affine_0_X"), file.getFloat("Affine_0_Y")));  
44 - if (file.contains("Affine_1_X") && file.contains("Affine_1_Y"))  
45 - landmarks.append(QPointF(file.getFloat("Affine_1_X"), file.getFloat("Affine_1_Y"))); 42 + if (file.contains("Affine_0")) landmarks.append(file.get<QPointF>("Affine_0"));
  43 + if (file.contains("Affine_1")) landmarks.append(file.get<QPointF>("Affine_1"));
46 while (landmarks.size() < NumLandmarks) 44 while (landmarks.size() < NumLandmarks)
47 landmarks.append(QPointF()); 45 landmarks.append(QPointF());
48 nearestLandmark = -1; 46 nearestLandmark = -1;
sdk/core/bee.cpp
@@ -71,7 +71,7 @@ FileList BEE::readSigset(const QString &amp;sigset, bool ignoreMetadata) @@ -71,7 +71,7 @@ FileList BEE::readSigset(const QString &amp;sigset, bool ignoreMetadata)
71 newFile.append(file); 71 newFile.append(file);
72 file = newFile; 72 file = newFile;
73 } else if (!ignoreMetadata) { 73 } else if (!ignoreMetadata) {
74 - file.insert(key, value); 74 + file.set(key, value);
75 } 75 }
76 } 76 }
77 77
@@ -99,7 +99,7 @@ void BEE::writeSigset(const QString &amp;sigset, const br::FileList &amp;files, bool ign @@ -99,7 +99,7 @@ void BEE::writeSigset(const QString &amp;sigset, const br::FileList &amp;files, bool ign
99 QStringList metadata; 99 QStringList metadata;
100 if (!ignoreMetadata) 100 if (!ignoreMetadata)
101 foreach (const QString &key, file.localKeys()) 101 foreach (const QString &key, file.localKeys())
102 - metadata.append(key+"=\""+file.getString(key, "?")+"\""); 102 + metadata.append(key+"=\""+file.get<QString>(key, "?")+"\"");
103 lines.append("\t<biometric-signature name=\"" + file.subject() +"\">"); 103 lines.append("\t<biometric-signature name=\"" + file.subject() +"\">");
104 lines.append("\t\t<presentation file-name=\"" + file.name + "\" " + metadata.join(" ") + "/>"); 104 lines.append("\t\t<presentation file-name=\"" + file.name + "\" " + metadata.join(" ") + "/>");
105 lines.append("\t</biometric-signature>"); 105 lines.append("\t</biometric-signature>");
@@ -113,19 +113,19 @@ Mat readMatrix(const br::File &amp;matrix) @@ -113,19 +113,19 @@ Mat readMatrix(const br::File &amp;matrix)
113 { 113 {
114 // Special case matrix construction 114 // Special case matrix construction
115 if (matrix == "Identity") { 115 if (matrix == "Identity") {
116 - int rows = matrix.getInt("rows", -1);  
117 - int columns = matrix.getInt("columns", -1);  
118 - const int size = matrix.getInt("size", -1); 116 + int rows = matrix.get<int>("rows", -1);
  117 + int columns = matrix.get<int>("columns", -1);
  118 + const int size = matrix.get<int>("size", -1);
119 if (size != -1) { 119 if (size != -1) {
120 if (rows == -1) rows = size; 120 if (rows == -1) rows = size;
121 if (columns == -1) columns = size; 121 if (columns == -1) columns = size;
122 } 122 }
123 - const int step = matrix.getInt("step", 1); 123 + const int step = matrix.get<int>("step", 1);
124 if (rows % step != 0) qFatal("Step does not divide rows evenly."); 124 if (rows % step != 0) qFatal("Step does not divide rows evenly.");
125 if (columns % step != 0) qFatal("Step does not divide columns evenly."); 125 if (columns % step != 0) qFatal("Step does not divide columns evenly.");
126 126
127 if (sizeof(T) == sizeof(BEE::Mask_t)) { 127 if (sizeof(T) == sizeof(BEE::Mask_t)) {
128 - const bool selfSimilar = matrix.getBool("selfSimilar"); 128 + const bool selfSimilar = matrix.get<bool>("selfSimilar", false);
129 129
130 Mat m(rows, columns, CV_8UC1); 130 Mat m(rows, columns, CV_8UC1);
131 m.setTo(BEE::NonMatch); 131 m.setTo(BEE::NonMatch);
@@ -171,8 +171,8 @@ Mat readMatrix(const br::File &amp;matrix) @@ -171,8 +171,8 @@ Mat readMatrix(const br::File &amp;matrix)
171 file.close(); 171 file.close();
172 172
173 Mat result; 173 Mat result;
174 - if (isDistance ^ matrix.getBool("negate")) m.convertTo(result, -1, -1);  
175 - else result = m.clone(); 174 + if (isDistance ^ matrix.get<bool>("negate", false)) m.convertTo(result, -1, -1);
  175 + else result = m.clone();
176 return result; 176 return result;
177 } 177 }
178 178
sdk/core/core.cpp
@@ -189,12 +189,14 @@ struct AlgorithmCore @@ -189,12 +189,14 @@ struct AlgorithmCore
189 enroll(file); 189 enroll(file);
190 gallery.reset(Gallery::make(getMemoryGallery(file))); 190 gallery.reset(Gallery::make(getMemoryGallery(file)));
191 galleryFiles = gallery->files(); 191 galleryFiles = gallery->files();
  192 +
  193 + qDebug() << galleryFiles;
192 } 194 }
193 } 195 }
194 196
195 void compare(File targetGallery, File queryGallery, File output) 197 void compare(File targetGallery, File queryGallery, File output)
196 { 198 {
197 - if (output.exists() && output.getBool("cache")) return; 199 + if (output.exists() && output.get<bool>("cache", false)) return;
198 if (queryGallery == ".") queryGallery = targetGallery; 200 if (queryGallery == ".") queryGallery = targetGallery;
199 201
200 QScopedPointer<Gallery> t, q; 202 QScopedPointer<Gallery> t, q;
@@ -250,7 +252,7 @@ private: @@ -250,7 +252,7 @@ private:
250 if (!file.isEmpty()) description = file; 252 if (!file.isEmpty()) description = file;
251 253
252 if (QFileInfo(description).exists()) { 254 if (QFileInfo(description).exists()) {
253 - if (Globals->verbose) qDebug("Loading %s", qPrintable(QFileInfo(description).fileName())); 255 + qDebug("Loading %s", qPrintable(QFileInfo(description).fileName()));
254 load(description); 256 load(description);
255 return; 257 return;
256 } 258 }
@@ -259,7 +261,7 @@ private: @@ -259,7 +261,7 @@ private:
259 if (Globals->abbreviations.contains(description)) 261 if (Globals->abbreviations.contains(description))
260 return init(Globals->abbreviations[description]); 262 return init(Globals->abbreviations[description]);
261 263
262 - QStringList words = description.split(':'); 264 + QStringList words = QtUtils::parse(description, ':');
263 if (words.size() > 2) qFatal("Invalid algorithm format."); 265 if (words.size() > 2) qFatal("Invalid algorithm format.");
264 266
265 transform = QSharedPointer<Transform>(Transform::make(words[0], NULL)); 267 transform = QSharedPointer<Transform>(Transform::make(words[0], NULL));
@@ -316,14 +318,14 @@ void br::Train(const File &amp;input, const File &amp;model) @@ -316,14 +318,14 @@ void br::Train(const File &amp;input, const File &amp;model)
316 { 318 {
317 qDebug("Training on %s%s", qPrintable(input.flat()), 319 qDebug("Training on %s%s", qPrintable(input.flat()),
318 model.isNull() ? "" : qPrintable(" to " + model.flat())); 320 model.isNull() ? "" : qPrintable(" to " + model.flat()));
319 - AlgorithmManager::getAlgorithm(model.getString("algorithm"))->train(input, model); 321 + AlgorithmManager::getAlgorithm(model.get<QString>("algorithm"))->train(input, model);
320 } 322 }
321 323
322 FileList br::Enroll(const File &input, const File &gallery) 324 FileList br::Enroll(const File &input, const File &gallery)
323 { 325 {
324 qDebug("Enrolling %s%s", qPrintable(input.flat()), 326 qDebug("Enrolling %s%s", qPrintable(input.flat()),
325 gallery.isNull() ? "" : qPrintable(" to " + gallery.flat())); 327 gallery.isNull() ? "" : qPrintable(" to " + gallery.flat()));
326 - return AlgorithmManager::getAlgorithm(gallery.getString("algorithm"))->enroll(input, gallery); 328 + return AlgorithmManager::getAlgorithm(gallery.get<QString>("algorithm"))->enroll(input, gallery);
327 } 329 }
328 330
329 void br::Compare(const File &targetGallery, const File &queryGallery, const File &output) 331 void br::Compare(const File &targetGallery, const File &queryGallery, const File &output)
@@ -331,7 +333,7 @@ void br::Compare(const File &amp;targetGallery, const File &amp;queryGallery, const File @@ -331,7 +333,7 @@ void br::Compare(const File &amp;targetGallery, const File &amp;queryGallery, const File
331 qDebug("Comparing %s and %s%s", qPrintable(targetGallery.flat()), 333 qDebug("Comparing %s and %s%s", qPrintable(targetGallery.flat()),
332 qPrintable(queryGallery.flat()), 334 qPrintable(queryGallery.flat()),
333 output.isNull() ? "" : qPrintable(" to " + output.flat())); 335 output.isNull() ? "" : qPrintable(" to " + output.flat()));
334 - AlgorithmManager::getAlgorithm(output.getString("algorithm"))->compare(targetGallery, queryGallery, output); 336 + AlgorithmManager::getAlgorithm(output.get<QString>("algorithm"))->compare(targetGallery, queryGallery, output);
335 } 337 }
336 338
337 void br::Convert(const File &src, const File &dst) 339 void br::Convert(const File &src, const File &dst)
sdk/core/plot.cpp
@@ -119,8 +119,8 @@ float Evaluate(const QString &amp;simmat, const QString &amp;mask, const QString &amp;csv) @@ -119,8 +119,8 @@ float Evaluate(const QString &amp;simmat, const QString &amp;mask, const QString &amp;csv)
119 // Read files 119 // Read files
120 const Mat scores = BEE::readSimmat(simmat); 120 const Mat scores = BEE::readSimmat(simmat);
121 File maskFile(mask); 121 File maskFile(mask);
122 - maskFile.insert("rows", scores.rows);  
123 - maskFile.insert("columns", scores.cols); 122 + maskFile.set("rows", scores.rows);
  123 + maskFile.set("columns", scores.cols);
124 const Mat masks = BEE::readMask(maskFile); 124 const Mat masks = BEE::readMask(maskFile);
125 if (scores.size() != masks.size()) qFatal("Simmat/Mask size mismatch."); 125 if (scores.size() != masks.size()) qFatal("Simmat/Mask size mismatch.");
126 126
@@ -143,6 +143,7 @@ float Evaluate(const QString &amp;simmat, const QString &amp;mask, const QString &amp;csv) @@ -143,6 +143,7 @@ float Evaluate(const QString &amp;simmat, const QString &amp;mask, const QString &amp;csv)
143 if (genuineCount == 0) qFatal("No genuine scores!"); 143 if (genuineCount == 0) qFatal("No genuine scores!");
144 if (impostorCount == 0) qFatal("No impostor scores!"); 144 if (impostorCount == 0) qFatal("No impostor scores!");
145 145
  146 + // Sort comparisons by simmat_val (score)
146 std::sort(comparisons.begin(), comparisons.end()); 147 std::sort(comparisons.begin(), comparisons.end());
147 148
148 double genuineSum = 0, impostorSum = 0; 149 double genuineSum = 0, impostorSum = 0;
@@ -155,8 +156,10 @@ float Evaluate(const QString &amp;simmat, const QString &amp;mask, const QString &amp;csv) @@ -155,8 +156,10 @@ float Evaluate(const QString &amp;simmat, const QString &amp;mask, const QString &amp;csv)
155 int index = 0; 156 int index = 0;
156 float minGenuineScore = std::numeric_limits<float>::max(); 157 float minGenuineScore = std::numeric_limits<float>::max();
157 float minImpostorScore = std::numeric_limits<float>::max(); 158 float minImpostorScore = std::numeric_limits<float>::max();
  159 +
158 while (index < comparisons.size()) { 160 while (index < comparisons.size()) {
159 float thresh = comparisons[index].score; 161 float thresh = comparisons[index].score;
  162 + // Compute genuine and imposter statistics at a threshold
160 while ((index < comparisons.size()) && 163 while ((index < comparisons.size()) &&
161 (comparisons[index].score == thresh)) { 164 (comparisons[index].score == thresh)) {
162 const Comparison &comparison = comparisons[index]; 165 const Comparison &comparison = comparisons[index];
@@ -205,7 +208,7 @@ float Evaluate(const QString &amp;simmat, const QString &amp;mask, const QString &amp;csv) @@ -205,7 +208,7 @@ float Evaluate(const QString &amp;simmat, const QString &amp;mask, const QString &amp;csv)
205 lines.append("Metadata,"+QString::number(impostorCount)+",Impostor"); 208 lines.append("Metadata,"+QString::number(impostorCount)+",Impostor");
206 lines.append("Metadata,"+QString::number(scores.cols*scores.rows-(genuineCount+impostorCount))+",Ignored"); 209 lines.append("Metadata,"+QString::number(scores.cols*scores.rows-(genuineCount+impostorCount))+",Ignored");
207 210
208 - // Write DET, PRE, REC 211 + // Write Detection Error Tradeoff (DET), PRE, REC
209 int points = qMin(operatingPoints.size(), Max_Points); 212 int points = qMin(operatingPoints.size(), Max_Points);
210 for (int i=0; i<points; i++) { 213 for (int i=0; i<points; i++) {
211 const OperatingPoint &operatingPoint = operatingPoints[double(i) / double(points-1) * double(operatingPoints.size()-1)]; 214 const OperatingPoint &operatingPoint = operatingPoints[double(i) / double(points-1) * double(operatingPoints.size()-1)];
@@ -416,7 +419,7 @@ struct RPlot @@ -416,7 +419,7 @@ struct RPlot
416 } 419 }
417 } 420 }
418 421
419 - const QString &smooth = destination.getString("smooth", ""); 422 + const QString &smooth = destination.get<QString>("smooth", "");
420 major.smooth = !smooth.isEmpty() && (major.header == smooth) && (major.size > 1); 423 major.smooth = !smooth.isEmpty() && (major.header == smooth) && (major.size > 1);
421 minor.smooth = !smooth.isEmpty() && (minor.header == smooth) && (minor.size > 1); 424 minor.smooth = !smooth.isEmpty() && (minor.header == smooth) && (minor.size > 1);
422 if (major.smooth) major.size = 1; 425 if (major.smooth) major.size = 1;
sdk/openbr_plugin.cpp
@@ -58,10 +58,10 @@ QString File::hash() const @@ -58,10 +58,10 @@ QString File::hash() const
58 return QtUtils::shortTextHash(flat()); 58 return QtUtils::shortTextHash(flat());
59 } 59 }
60 60
61 -void File::append(const QHash<QString, QVariant> &metadata) 61 +void File::append(const QMap<QString,QVariant> &metadata)
62 { 62 {
63 foreach (const QString &key, metadata.keys()) 63 foreach (const QString &key, metadata.keys())
64 - insert(key, metadata[key]); 64 + set(key, metadata[key]);
65 } 65 }
66 66
67 void File::append(const File &other) 67 void File::append(const File &other)
@@ -70,7 +70,7 @@ void File::append(const File &amp;other) @@ -70,7 +70,7 @@ void File::append(const File &amp;other)
70 if (name.isEmpty()) { 70 if (name.isEmpty()) {
71 name = other.name; 71 name = other.name;
72 } else { 72 } else {
73 - if (!contains("separator")) insert("separator", ";"); 73 + if (!contains("separator")) set("separator", ";");
74 name += value("separator").toString() + other.name; 74 name += value("separator").toString() + other.name;
75 } 75 }
76 } 76 }
@@ -110,29 +110,6 @@ QVariant File::value(const QString &amp;key) const @@ -110,29 +110,6 @@ QVariant File::value(const QString &amp;key) const
110 return m_metadata.contains(key) ? m_metadata.value(key) : Globals->property(qPrintable(key)); 110 return m_metadata.contains(key) ? m_metadata.value(key) : Globals->property(qPrintable(key));
111 } 111 }
112 112
113 -QString File::subject(int label)  
114 -{  
115 - return Globals->classes.key(label, QString::number(label));  
116 -}  
117 -  
118 -float File::label() const  
119 -{  
120 - const QVariant variant = value("Label");  
121 - if (variant.isNull()) return -1;  
122 -  
123 - if (Globals->classes.contains(variant.toString()))  
124 - return Globals->classes.value(variant.toString());  
125 -  
126 - bool ok;  
127 - const float val = variant.toFloat(&ok);  
128 - return ok ? val : -1;  
129 -}  
130 -  
131 -void File::remove(const QString &key)  
132 -{  
133 - m_metadata.remove(key);  
134 -}  
135 -  
136 void File::set(const QString &key, const QVariant &value) 113 void File::set(const QString &key, const QVariant &value)
137 { 114 {
138 if (key == "Label") { 115 if (key == "Label") {
@@ -153,155 +130,90 @@ void File::set(const QString &amp;key, const QVariant &amp;value) @@ -153,155 +130,90 @@ void File::set(const QString &amp;key, const QVariant &amp;value)
153 m_metadata.insert(key, value); 130 m_metadata.insert(key, value);
154 } 131 }
155 132
156 -QVariant File::get(const QString &key) const  
157 -{  
158 - if (!contains(key)) qFatal("Missing key: %s", qPrintable(key));  
159 - return value(key);  
160 -}  
161 -  
162 -QVariant File::get(const QString &key, const QVariant &defaultValue) const  
163 -{  
164 - if (!contains(key)) return defaultValue;  
165 - return value(key);  
166 -}  
167 -  
168 -bool File::getBool(const QString &key) const  
169 -{  
170 - if (!contains(key)) return false;  
171 - QString v = value(key).toString();  
172 - if (v.isEmpty() || (v == "true")) return true;  
173 - if (v == "false") return false;  
174 - return v.toInt();  
175 -}  
176 -  
177 -void File::setBool(const QString &key, bool value)  
178 -{  
179 - if (value) m_metadata.insert(key, QVariant());  
180 - else m_metadata.remove(key);  
181 -}  
182 -  
183 -int File::getInt(const QString &key) const  
184 -{  
185 - if (!contains(key)) qFatal("Missing key: %s", qPrintable(key));  
186 - bool ok; int result = value(key).toInt(&ok);  
187 - if (!ok) qFatal("Invalid conversion from: %s", qPrintable(getString(key)));  
188 - return result;  
189 -}  
190 -  
191 -int File::getInt(const QString &key, int defaultValue) const  
192 -{  
193 - if (!contains(key)) return defaultValue;  
194 - bool ok; int result = value(key).toInt(&ok);  
195 - if (!ok) return defaultValue;  
196 - return result;  
197 -}  
198 -  
199 -float File::getFloat(const QString &key) const  
200 -{  
201 - if (!contains(key)) qFatal("Missing key: %s", qPrintable(key));  
202 - bool ok; float result = value(key).toFloat(&ok);  
203 - if (!ok) qFatal("Invalid conversion from: %s", qPrintable(getString(key)));  
204 - return result;  
205 -}  
206 -  
207 -float File::getFloat(const QString &key, float defaultValue) const 133 +QString File::subject(int label)
208 { 134 {
209 - if (!contains(key)) return defaultValue;  
210 - bool ok; float result = value(key).toFloat(&ok);  
211 - if (!ok) return defaultValue;  
212 - return result; 135 + return Globals->classes.key(label, QString::number(label));
213 } 136 }
214 137
215 -QString File::getString(const QString &key) const 138 +float File::label() const
216 { 139 {
217 - if (!contains(key)) qFatal("Missing key: %s", qPrintable(key));  
218 - return value(key).toString();  
219 -} 140 + const QVariant variant = value("Label");
  141 + if (variant.isNull()) return -1;
220 142
221 -QString File::getString(const QString &key, const QString &defaultValue) const  
222 -{  
223 - if (!contains(key)) return defaultValue;  
224 - return value(key).toString();  
225 -} 143 + if (Globals->classes.contains(variant.toString()))
  144 + return Globals->classes.value(variant.toString());
226 145
227 -QList<QPointF> File::landmarks() const  
228 -{  
229 - QList<QPointF> landmarks;  
230 - foreach (const QVariant &landmark, value("Landmarks").toList())  
231 - landmarks.append(landmark.toPointF());  
232 - return landmarks; 146 + bool ok;
  147 + const float val = variant.toFloat(&ok);
  148 + return ok ? val : -1;
233 } 149 }
234 150
235 -QList<QPointF> File::namedLandmarks() const 151 +QList<QPointF> File::namedPoints() const
236 { 152 {
237 QList<QPointF> landmarks; 153 QList<QPointF> landmarks;
238 - QStringList keys = localMetadata().keys();  
239 - foreach (const QString &key, keys) {  
240 - if (!key.endsWith("_X"))  
241 - continue;  
242 - QString keyBaseName = key.left(key.size()-2);  
243 - if (!keys.contains(keyBaseName+"_Y") ||  
244 - keys.contains(keyBaseName+"_Width") ||  
245 - keys.contains(keyBaseName+"_Height") ||  
246 - keys.contains(keyBaseName+"_Radius"))  
247 - continue;  
248 - landmarks.append(QPointF(getFloat(keyBaseName+"_X"), getFloat(keyBaseName+"_Y"))); 154 + foreach (const QString &key, localMetadata().keys()) {
  155 + const QVariant &variant = m_metadata[key];
  156 + if (variant.canConvert<QPointF>())
  157 + landmarks.append(variant.value<QPointF>());
249 } 158 }
250 return landmarks; 159 return landmarks;
251 } 160 }
252 161
253 -void File::appendLandmark(const QPointF &landmark) 162 +QList<QPointF> File::points() const
254 { 163 {
255 - QList<QVariant> newLandmarks = m_metadata["Landmarks"].toList();  
256 - newLandmarks.append(landmark);  
257 - m_metadata["Landmarks"] = newLandmarks; 164 + QList<QPointF> points;
  165 + foreach (const QVariant &point, m_metadata["Points"].toList())
  166 + points.append(point.toPointF());
  167 + return points;
258 } 168 }
259 169
260 -void File::appendLandmarks(const QList<QPointF> &landmarks) 170 +void File::appendPoint(const QPointF &point)
261 { 171 {
262 - QList<QVariant> newLandmarks = m_metadata["Landmarks"].toList();  
263 - foreach (const QPointF &landmark, landmarks)  
264 - newLandmarks.append(landmark);  
265 - m_metadata["Landmarks"] = newLandmarks; 172 + QList<QVariant> newPoints = m_metadata["Points"].toList();
  173 + newPoints.append(point);
  174 + m_metadata["Points"] = newPoints;
266 } 175 }
267 176
268 -void File::setLandmarks(const QList<QPointF> &landmarks) 177 +void File::appendPoints(const QList<QPointF> &points)
269 { 178 {
270 - QList<QVariant> landmarkList; landmarkList.reserve(landmarks.size());  
271 - foreach (const QPointF &landmark, landmarks)  
272 - landmarkList.append(landmark);  
273 - m_metadata["Landmarks"] = landmarkList; 179 + QList<QVariant> newPoints = m_metadata["Points"].toList();
  180 + foreach (const QPointF &point, points)
  181 + newPoints.append(point);
  182 + m_metadata["Points"] = newPoints;
274 } 183 }
275 184
276 -QList<QRectF> File::ROIs() const 185 +QList<QRectF> File::namedRects() const
277 { 186 {
278 - QList<QRectF> ROIs;  
279 - foreach (const QVariant &ROI, value("ROIs").toList())  
280 - ROIs.append(ROI.toRect());  
281 - return ROIs; 187 + QList<QRectF> rects;
  188 + foreach (const QString &key, localMetadata().keys()) {
  189 + const QVariant &variant = m_metadata[key];
  190 + if (variant.canConvert<QRectF>())
  191 + rects.append(variant.value<QRectF>());
  192 + }
  193 + return rects;
282 } 194 }
283 195
284 -void File::appendROI(const QRectF &ROI) 196 +QList<QRectF> File::rects() const
285 { 197 {
286 - QList<QVariant> newROIs = m_metadata["ROIs"].toList();  
287 - newROIs.append(ROI);  
288 - m_metadata["ROIs"] = newROIs; 198 + QList<QRectF> rects;
  199 + foreach (const QVariant &rect, m_metadata["Rects"].toList())
  200 + rects.append(rect.toRect());
  201 + return rects;
289 } 202 }
290 203
291 -void File::appendROIs(const QList<QRectF> &ROIs) 204 +void File::appendRect(const QRectF &rect)
292 { 205 {
293 - QList<QVariant> newROIs = m_metadata["ROIs"].toList();  
294 - foreach (const QRectF &ROI, ROIs)  
295 - newROIs.append(ROI);  
296 - m_metadata["ROIs"] = newROIs; 206 + QList<QVariant> newRects = m_metadata["Rects"].toList();
  207 + newRects.append(rect);
  208 + m_metadata["Rects"] = newRects;
297 } 209 }
298 210
299 -void File::setROIs(const QList<QRectF> &ROIs) 211 +void File::appendRects(const QList<QRectF> &rects)
300 { 212 {
301 - QList<QVariant> ROIList; ROIList.reserve(ROIs.size());  
302 - foreach (const QRectF &ROI, ROIs)  
303 - ROIList.append(ROI);  
304 - m_metadata["ROIs"] = ROIList; 213 + QList<QVariant> newRects = m_metadata["Rects"].toList();
  214 + foreach (const QRectF &rect, rects)
  215 + newRects.append(rect);
  216 + m_metadata["Rects"] = newRects;
305 } 217 }
306 218
307 /* File - private methods */ 219 /* File - private methods */
@@ -325,10 +237,10 @@ void File::init(const QString &amp;file) @@ -325,10 +237,10 @@ void File::init(const QString &amp;file)
325 QStringList words = QtUtils::parse(parameters[i], '='); 237 QStringList words = QtUtils::parse(parameters[i], '=');
326 QtUtils::checkArgsSize("File", words, 1, 2); 238 QtUtils::checkArgsSize("File", words, 1, 2);
327 if (words.size() < 2) { 239 if (words.size() < 2) {
328 - if (unnamed) insertParameter(i, words[0]);  
329 - else insert(words[0], QVariant()); 240 + if (unnamed) setParameter(i, words[0]);
  241 + else set(words[0], QVariant());
330 } else { 242 } else {
331 - insert(words[0], words[1]); 243 + set(words[0], words[1]);
332 } 244 }
333 } 245 }
334 name = name.left(index); 246 name = name.left(index);
@@ -391,9 +303,8 @@ void FileList::sort(const QString&amp; key) @@ -391,9 +303,8 @@ void FileList::sort(const QString&amp; key)
391 FileList sortedList; 303 FileList sortedList;
392 304
393 for (int i = 0; i < size(); i++) { 305 for (int i = 0; i < size(); i++) {
394 - if (at(i).contains(key)) {  
395 - metadata.append(at(i).get(key).toString());  
396 - } 306 + if (at(i).contains(key))
  307 + metadata.append(at(i).get<QString>(key));
397 else sortedList.push_back(at(i)); 308 else sortedList.push_back(at(i));
398 } 309 }
399 310
@@ -416,7 +327,7 @@ QList&lt;int&gt; FileList::crossValidationPartitions() const @@ -416,7 +327,7 @@ QList&lt;int&gt; FileList::crossValidationPartitions() const
416 { 327 {
417 QList<int> crossValidationPartitions; crossValidationPartitions.reserve(size()); 328 QList<int> crossValidationPartitions; crossValidationPartitions.reserve(size());
418 foreach (const File &f, *this) 329 foreach (const File &f, *this)
419 - crossValidationPartitions.append(f.getInt("Cross_Validation_Partition", 0)); 330 + crossValidationPartitions.append(f.get<int>("Cross_Validation_Partition", 0));
420 return crossValidationPartitions; 331 return crossValidationPartitions;
421 } 332 }
422 333
@@ -424,7 +335,7 @@ int FileList::failures() const @@ -424,7 +335,7 @@ int FileList::failures() const
424 { 335 {
425 int failures = 0; 336 int failures = 0;
426 foreach (const File &file, *this) 337 foreach (const File &file, *this)
427 - if (file.getBool("FTO") || file.getBool("FTE")) 338 + if (file.get<bool>("FTO", false) || file.get<bool>("FTE", false))
428 failures++; 339 failures++;
429 return failures; 340 return failures;
430 } 341 }
@@ -447,9 +358,9 @@ TemplateList TemplateList::fromGallery(const br::File &amp;gallery) @@ -447,9 +358,9 @@ TemplateList TemplateList::fromGallery(const br::File &amp;gallery)
447 foreach (const br::File &file, gallery.split()) { 358 foreach (const br::File &file, gallery.split()) {
448 QScopedPointer<Gallery> i(Gallery::make(file)); 359 QScopedPointer<Gallery> i(Gallery::make(file));
449 TemplateList newTemplates = i->read(); 360 TemplateList newTemplates = i->read();
450 - newTemplates = newTemplates.mid(gallery.getInt("pos", 0), gallery.getInt("length", -1));  
451 - if (gallery.getBool("reduce")) newTemplates = newTemplates.reduced();  
452 - const int crossValidate = gallery.getInt("crossValidate"); 361 + newTemplates = newTemplates.mid(gallery.get<int>("pos", 0), gallery.get<int>("length", -1));
  362 + if (gallery.get<bool>("reduce", false)) newTemplates = newTemplates.reduced();
  363 + const int crossValidate = gallery.get<int>("crossValidate");
453 if (crossValidate > 0) srand(0); 364 if (crossValidate > 0) srand(0);
454 365
455 // If file is a Format not a Gallery 366 // If file is a Format not a Gallery
@@ -460,11 +371,11 @@ TemplateList TemplateList::fromGallery(const br::File &amp;gallery) @@ -460,11 +371,11 @@ TemplateList TemplateList::fromGallery(const br::File &amp;gallery)
460 for (int i=0; i<newTemplates.size(); i++) { 371 for (int i=0; i<newTemplates.size(); i++) {
461 newTemplates[i].file.append(gallery.localMetadata()); 372 newTemplates[i].file.append(gallery.localMetadata());
462 newTemplates[i].file.append(file.localMetadata()); 373 newTemplates[i].file.append(file.localMetadata());
463 - newTemplates[i].file.insert("Index", i+templates.size());  
464 - if (crossValidate > 0) newTemplates[i].file.insert("Cross_Validation_Partition", rand()%crossValidate); 374 + newTemplates[i].file.set("Index", i+templates.size());
  375 + if (crossValidate > 0) newTemplates[i].file.set("Cross_Validation_Partition", rand()%crossValidate);
465 } 376 }
466 377
467 - if (!templates.isEmpty() && gallery.getBool("merge")) { 378 + if (!templates.isEmpty() && gallery.get<bool>("merge", false)) {
468 if (newTemplates.size() != templates.size()) 379 if (newTemplates.size() != templates.size())
469 qFatal("Inputs must be the same size in order to merge."); 380 qFatal("Inputs must be the same size in order to merge.");
470 for (int i=0; i<templates.size(); i++) 381 for (int i=0; i<templates.size(); i++)
@@ -494,7 +405,8 @@ TemplateList TemplateList::relabel(const TemplateList &amp;tl) @@ -494,7 +405,8 @@ TemplateList TemplateList::relabel(const TemplateList &amp;tl)
494 QStringList Object::parameters() const 405 QStringList Object::parameters() const
495 { 406 {
496 QStringList parameters; 407 QStringList parameters;
497 - for (int i=metaObject()->propertyOffset(); i<metaObject()->propertyCount(); i++) { 408 +
  409 + for (int i = firstAvailablePropertyIdx; i < metaObject()->propertyCount();i++) {
498 QMetaProperty property = metaObject()->property(i); 410 QMetaProperty property = metaObject()->property(i);
499 if (property.isStored(this)) continue; 411 if (property.isStored(this)) continue;
500 parameters.append(QString("%1 %2 = %3").arg(property.typeName(), property.name(), property.read(this).toString())); 412 parameters.append(QString("%1 %2 = %3").arg(property.typeName(), property.name(), property.read(this).toString()));
@@ -713,18 +625,37 @@ void Object::init(const File &amp;file_) @@ -713,18 +625,37 @@ void Object::init(const File &amp;file_)
713 // Set name 625 // Set name
714 QString name = metaObject()->className(); 626 QString name = metaObject()->className();
715 if (name.startsWith("br::")) name = name.right(name.size()-4); 627 if (name.startsWith("br::")) name = name.right(name.size()-4);
716 - const QMetaObject *superClass = metaObject()->superClass(); 628 +
  629 + firstAvailablePropertyIdx = metaObject()->propertyCount();
  630 +
  631 + const QMetaObject * baseClass = metaObject();
  632 + const QMetaObject * superClass = metaObject()->superClass();
  633 +
717 while (superClass != NULL) { 634 while (superClass != NULL) {
  635 + const QMetaObject * nextClass = superClass->superClass();
  636 +
  637 + // baseClass <- something <- br::Object
  638 + // baseClass is the highest class whose properties we can set via positional arguments
  639 + if (nextClass && !strcmp(nextClass->className(),"br::Object")) {
  640 + firstAvailablePropertyIdx = baseClass->propertyOffset();
  641 + }
  642 +
718 QString superClassName = superClass->className(); 643 QString superClassName = superClass->className();
  644 +
  645 + // strip br:: prefix from superclass name
719 if (superClassName.startsWith("br::")) 646 if (superClassName.startsWith("br::"))
720 superClassName = superClassName.right(superClassName.size()-4); 647 superClassName = superClassName.right(superClassName.size()-4);
  648 +
  649 + // Strip superclass name from base class name (e.g. PipeTransform -> Pipe)
721 if (name.endsWith(superClassName)) 650 if (name.endsWith(superClassName))
722 name = name.left(name.size() - superClassName.size()); 651 name = name.left(name.size() - superClassName.size());
  652 + baseClass = superClass;
723 superClass = superClass->superClass(); 653 superClass = superClass->superClass();
  654 +
724 } 655 }
725 setObjectName(name); 656 setObjectName(name);
726 657
727 - // Set properties 658 + // Reset all properties
728 for (int i=0; i<metaObject()->propertyCount(); i++) { 659 for (int i=0; i<metaObject()->propertyCount(); i++) {
729 QMetaProperty property = metaObject()->property(i); 660 QMetaProperty property = metaObject()->property(i);
730 if (property.isResettable()) 661 if (property.isResettable())
@@ -734,8 +665,17 @@ void Object::init(const File &amp;file_) @@ -734,8 +665,17 @@ void Object::init(const File &amp;file_)
734 665
735 foreach (QString key, file.localKeys()) { 666 foreach (QString key, file.localKeys()) {
736 const QString value = file.value(key).toString(); 667 const QString value = file.value(key).toString();
737 - if (key.startsWith("_Arg"))  
738 - key = metaObject()->property(metaObject()->propertyOffset()+key.mid(4).toInt()).name(); 668 +
  669 + if (key.startsWith(("_Arg"))) {
  670 + int argument_number = key.mid(4).toInt();
  671 + int target_idx = argument_number + firstAvailablePropertyIdx;
  672 +
  673 + if (target_idx >= metaObject()->propertyCount()) {
  674 + qWarning("too many arguments for transform, ignoring %s\n", qPrintable(value));
  675 + continue;
  676 + }
  677 + key = metaObject()->property(target_idx).name();
  678 + }
739 setProperty(key, value); 679 setProperty(key, value);
740 } 680 }
741 681
@@ -1107,7 +1047,7 @@ static TemplateList Downsample(const TemplateList &amp;templates, const Transform *t @@ -1107,7 +1047,7 @@ static TemplateList Downsample(const TemplateList &amp;templates, const Transform *t
1107 const int selectedLabel = selectedLabels[i]; 1047 const int selectedLabel = selectedLabels[i];
1108 QList<int> indices; 1048 QList<int> indices;
1109 for (int j=0; j<allLabels.size(); j++) 1049 for (int j=0; j<allLabels.size(); j++)
1110 - if ((allLabels[j] == selectedLabel) && (!templates.value(j).file.getBool("FTE"))) 1050 + if ((allLabels[j] == selectedLabel) && (!templates.value(j).file.get<bool>("FTE", false)))
1111 indices.append(j); 1051 indices.append(j);
1112 1052
1113 std::random_shuffle(indices.begin(), indices.end()); 1053 std::random_shuffle(indices.begin(), indices.end());
@@ -1291,7 +1231,7 @@ static void _project(const Transform *transform, const Template *src, Template * @@ -1291,7 +1231,7 @@ static void _project(const Transform *transform, const Template *src, Template *
1291 } catch (...) { 1231 } catch (...) {
1292 qWarning("Exception triggered when processing %s with transform %s", qPrintable(src->file.flat()), qPrintable(transform->objectName())); 1232 qWarning("Exception triggered when processing %s with transform %s", qPrintable(src->file.flat()), qPrintable(transform->objectName()));
1293 *dst = Template(src->file); 1233 *dst = Template(src->file);
1294 - dst->file.setBool("FTE"); 1234 + dst->file.set("FTE", true);
1295 } 1235 }
1296 } 1236 }
1297 1237
@@ -1302,7 +1242,7 @@ static void _backProject(const Transform *transform, const Template *dst, Templa @@ -1302,7 +1242,7 @@ static void _backProject(const Transform *transform, const Template *dst, Templa
1302 } catch (...) { 1242 } catch (...) {
1303 qWarning("Exception triggered when processing %s with transform %s", qPrintable(src->file.flat()), qPrintable(transform->objectName())); 1243 qWarning("Exception triggered when processing %s with transform %s", qPrintable(src->file.flat()), qPrintable(transform->objectName()));
1304 *src = Template(dst->file); 1244 *src = Template(dst->file);
1305 - src->file.setBool("FTE"); 1245 + src->file.set("FTE", true);
1306 } 1246 }
1307 } 1247 }
1308 1248
sdk/openbr_plugin.h
@@ -61,6 +61,7 @@ @@ -61,6 +61,7 @@
61 * 61 *
62 * \section examples Examples 62 * \section examples Examples
63 * - \ref cpp_face_recognition 63 * - \ref cpp_face_recognition
  64 + * - \ref cpp_face_recognition_search
64 * - \ref cpp_age_estimation 65 * - \ref cpp_age_estimation
65 * - \ref cpp_gender_estimation 66 * - \ref cpp_gender_estimation
66 * 67 *
@@ -68,6 +69,10 @@ @@ -68,6 +69,10 @@
68 * \ref cli_face_recognition "Command Line Interface Equivalent" 69 * \ref cli_face_recognition "Command Line Interface Equivalent"
69 * \snippet app/examples/face_recognition.cpp face_recognition 70 * \snippet app/examples/face_recognition.cpp face_recognition
70 * 71 *
  72 + * \subsection cpp_face_recognition_search Face Recognition Search
  73 + * \ref cli_face_recognition_search "Command Line Interface Equivalent"
  74 + * \snippet app/examples/face_recognition_search.cpp face_recognition_search
  75 + *
71 * \subsection cpp_age_estimation Age Estimation 76 * \subsection cpp_age_estimation Age Estimation
72 * \ref cli_age_estimation "Command Line Interface Equivalent" 77 * \ref cli_age_estimation "Command Line Interface Equivalent"
73 * \snippet app/examples/age_estimation.cpp age_estimation 78 * \snippet app/examples/age_estimation.cpp age_estimation
@@ -123,10 +128,8 @@ void reset_##NAME() { NAME = DEFAULT; } @@ -123,10 +128,8 @@ void reset_##NAME() { NAME = DEFAULT; }
123 * 128 *
124 * Key | Value | Description 129 * Key | Value | Description
125 * --- | ---- | ----------- 130 * --- | ---- | -----------
126 - * path | QString | Resolve complete file paths from file names  
127 - * enrollAll | bool | Enroll zero or more templates per file  
128 * separator | QString | Seperate #name into multiple files 131 * separator | QString | Seperate #name into multiple files
129 - * Index | int | Index of a template in a template list 132 + * Index | int | Index of a template in a template list
130 * Label | float | Classification/Regression class 133 * Label | float | Classification/Regression class
131 * Confidence | float | Classification/Regression quality 134 * Confidence | float | Classification/Regression quality
132 * FTE | bool | Failure to enroll 135 * FTE | bool | Failure to enroll
@@ -140,8 +143,8 @@ void reset_##NAME() { NAME = DEFAULT; } @@ -140,8 +143,8 @@ void reset_##NAME() { NAME = DEFAULT; }
140 * Roll | float | Pose 143 * Roll | float | Pose
141 * Pitch | float | Pose 144 * Pitch | float | Pose
142 * Yaw | float | Pose 145 * Yaw | float | Pose
143 - * Landmarks | QList<QPointF> | Landmark list  
144 - * ROIs | QList<Rect> | Region Of Interest (ROI) list 146 + * Points | QList<QPointF> | List of unnamed points
  147 + * Rects | QList<Rect> | List of unnamed rects
145 * Age | QString | Age used for demographic filtering 148 * Age | QString | Age used for demographic filtering
146 * _* | * | Reserved for internal use 149 * _* | * | Reserved for internal use
147 */ 150 */
@@ -151,24 +154,26 @@ struct BR_EXPORT File @@ -151,24 +154,26 @@ struct BR_EXPORT File
151 154
152 File() {} 155 File() {}
153 File(const QString &file) { init(file); } /*!< \brief Construct a file from a string. */ 156 File(const QString &file) { init(file); } /*!< \brief Construct a file from a string. */
154 - File(const QString &file, const QVariant &label) { init(file); insert("Label", label); } /*!< \brief Construct a file from a string and assign a label. */ 157 + File(const QString &file, const QVariant &label) { init(file); set("Label", label); } /*!< \brief Construct a file from a string and assign a label. */
155 File(const char *file) { init(file); } /*!< \brief Construct a file from a c-style string. */ 158 File(const char *file) { init(file); } /*!< \brief Construct a file from a c-style string. */
156 inline operator QString() const { return name; } /*!< \brief Returns #name. */ 159 inline operator QString() const { return name; } /*!< \brief Returns #name. */
157 QString flat() const; /*!< \brief A stringified version of the file with metadata. */ 160 QString flat() const; /*!< \brief A stringified version of the file with metadata. */
158 QString hash() const; /*!< \brief A hash of the file. */ 161 QString hash() const; /*!< \brief A hash of the file. */
159 - inline void clear() { name.clear(); m_metadata.clear(); } /*!< \brief Clears the file's name and metadata. */  
160 162
161 inline QList<QString> localKeys() const { return m_metadata.keys(); } /*!< \brief Returns the private metadata keys. */ 163 inline QList<QString> localKeys() const { return m_metadata.keys(); } /*!< \brief Returns the private metadata keys. */
162 - inline QHash<QString,QVariant> localMetadata() const { return m_metadata; } /*!< \brief Returns the private metadata. */  
163 - inline void insert(const QString &key, const QVariant &value) { set(key, value); } /*!< \brief Equivalent to set(). */  
164 - void append(const QHash<QString,QVariant> &localMetadata); /*!< \brief Add new metadata fields. */ 164 + inline QMap<QString,QVariant> localMetadata() const { return m_metadata; } /*!< \brief Returns the private metadata. */
  165 +
  166 + void append(const QMap<QString,QVariant> &localMetadata); /*!< \brief Add new metadata fields. */
165 void append(const File &other); /*!< \brief Append another file using \c separator. */ 167 void append(const File &other); /*!< \brief Append another file using \c separator. */
  168 + inline File &operator+=(const QMap<QString,QVariant> &other) { append(other); return *this; } /*!< \brief Add new metadata fields. */
  169 + inline File &operator+=(const File &other) { append(other); return *this; } /*!< \brief Append another file using \c separator. */
  170 +
166 QList<File> split() const; /*!< \brief Split the file using \c separator. */ 171 QList<File> split() const; /*!< \brief Split the file using \c separator. */
167 QList<File> split(const QString &separator) const; /*!< \brief Split the file. */ 172 QList<File> split(const QString &separator) const; /*!< \brief Split the file. */
168 173
169 - inline void insertParameter(int index, const QVariant &value) { insert("_Arg" + QString::number(index), value); } /*!< \brief Insert a keyless value. */  
170 - inline bool containsParameter(int index) const { return m_metadata.contains("_Arg" + QString::number(index)); } /*!< \brief Check for the existence of a keyless value. */  
171 - inline QVariant parameter(int index) const { return m_metadata.value("_Arg" + QString::number(index)); } /*!< \brief Retrieve a keyless value. */ 174 + inline void setParameter(int index, const QVariant &value) { set("_Arg" + QString::number(index), value); } /*!< \brief Insert a keyless value. */
  175 + inline bool containsParameter(int index) const { return contains("_Arg" + QString::number(index)); } /*!< \brief Check for the existence of a keyless value. */
  176 + inline QVariant getParameter(int index) const { return get<QVariant>("_Arg" + QString::number(index)); } /*!< \brief Retrieve a keyless value. */
172 177
173 inline bool operator==(const char* other) const { return name == other; } /*!< \brief Compare name to c-style string. */ 178 inline bool operator==(const char* other) const { return name == other; } /*!< \brief Compare name to c-style string. */
174 inline bool operator==(const File &other) const { return (name == other.name) && (m_metadata == other.m_metadata); } /*!< \brief Compare name and metadata for equality. */ 179 inline bool operator==(const File &other) const { return (name == other.name) && (m_metadata == other.m_metadata); } /*!< \brief Compare name and metadata for equality. */
@@ -177,8 +182,6 @@ struct BR_EXPORT File @@ -177,8 +182,6 @@ struct BR_EXPORT File
177 inline bool operator<=(const File &other) const { return name <= other.name; } /*!< \brief Compare name. */ 182 inline bool operator<=(const File &other) const { return name <= other.name; } /*!< \brief Compare name. */
178 inline bool operator>(const File &other) const { return name > other.name; } /*!< \brief Compare name. */ 183 inline bool operator>(const File &other) const { return name > other.name; } /*!< \brief Compare name. */
179 inline bool operator>=(const File &other) const { return name >= other.name; } /*!< \brief Compare name. */ 184 inline bool operator>=(const File &other) const { return name >= other.name; } /*!< \brief Compare name. */
180 - inline File &operator+=(const QHash<QString,QVariant> &other) { append(other); return *this; } /*!< \brief Add new metadata fields. */  
181 - inline File &operator+=(const File &other) { append(other); return *this; } /*!< \brief Append another file using \c separator. */  
182 185
183 inline bool isNull() const { return name.isEmpty() && m_metadata.isEmpty(); } /*!< \brief Returns \c true if name and metadata are empty, \c false otherwise. */ 186 inline bool isNull() const { return name.isEmpty() && m_metadata.isEmpty(); } /*!< \brief Returns \c true if name and metadata are empty, \c false otherwise. */
184 inline bool isTerminal() const { return name == "terminal"; } /*!< \brief Returns \c true if #name is "terminal", \c false otherwise. */ 187 inline bool isTerminal() const { return name == "terminal"; } /*!< \brief Returns \c true if #name is "terminal", \c false otherwise. */
@@ -191,42 +194,53 @@ struct BR_EXPORT File @@ -191,42 +194,53 @@ struct BR_EXPORT File
191 194
192 bool contains(const QString &key) const; /*!< \brief Returns \c true if the key has an associated value, \c false otherwise. */ 195 bool contains(const QString &key) const; /*!< \brief Returns \c true if the key has an associated value, \c false otherwise. */
193 QVariant value(const QString &key) const; /*!< \brief Returns the value for the specified key. */ 196 QVariant value(const QString &key) const; /*!< \brief Returns the value for the specified key. */
  197 + void set(const QString &key, const QVariant &value); /*!< \brief Insert or overwrite the metadata key with the specified value. */
  198 + inline void remove(const QString &key) { m_metadata.remove(key); } /*!< \brief Remove the metadata key. */
  199 +
  200 + /*!< \brief Returns a value for the key, throwing an error if the key does not exist. */
  201 + template <typename T>
  202 + T get(const QString &key) const
  203 + {
  204 + if (!contains(key)) qFatal("Missing key: %s", qPrintable(key));
  205 + QVariant variant = value(key);
  206 + if (!variant.canConvert<T>()) qFatal("Can't convert: %s", qPrintable(key));
  207 + return variant.value<T>();
  208 + }
  209 +
  210 + /*!< \brief Returns a value for the key, returning \em defaultValue if the key does not exist or can't be converted. */
  211 + template <typename T>
  212 + T get(const QString &key, const T &defaultValue) const
  213 + {
  214 + if (!contains(key)) return defaultValue;
  215 + QVariant variant = value(key);
  216 + if (!variant.canConvert<T>()) return defaultValue;
  217 + return variant.value<T>();
  218 + }
  219 +
194 static QString subject(int label); /*!< \brief Looks up the subject for the provided label. */ 220 static QString subject(int label); /*!< \brief Looks up the subject for the provided label. */
195 inline QString subject() const { return subject(label()); } /*!< \brief Looks up the subject from the file's label. */ 221 inline QString subject() const { return subject(label()); } /*!< \brief Looks up the subject from the file's label. */
196 - inline bool failed() const { return getBool("FTE") || getBool("FTO"); } /*!< \brief Returns \c true if the file failed to open or enroll, \c false otherwise. */  
197 -  
198 - void remove(const QString &key); /*!< \brief Remove the metadata key. */  
199 - void set(const QString &key, const QVariant &value); /*!< \brief Insert or overwrite the metadata key with the specified value. */  
200 - QVariant get(const QString &key) const; /*!< \brief Returns a QVariant for the key, throwing an error if the key does not exist. */  
201 - QVariant get(const QString &key, const QVariant &value) const; /*!< \brief Returns a QVariant for the key, returning \em defaultValue if the key does not exist. */  
202 float label() const; /*!< \brief Convenience function for retrieving the file's \c Label. */ 222 float label() const; /*!< \brief Convenience function for retrieving the file's \c Label. */
203 - inline void setLabel(float label) { insert("Label", label); } /*!< \brief Convenience function for setting the file's \c Label. */  
204 - bool getBool(const QString &key) const; /*!< \brief Returns a boolean value for the key. */  
205 - void setBool(const QString &key, bool value = true); /*!< \brief Sets a boolean value for the key. */  
206 - int getInt(const QString &key) const; /*!< \brief Returns an int value for the key, throwing an error if the key does not exist. */  
207 - int getInt(const QString &key, int defaultValue) const; /*!< \brief Returns an int value for the key, returning \em defaultValue if the key does not exist. */  
208 - float getFloat(const QString &key) const; /*!< \brief Returns a float value for the key, throwing an error if the key does not exist. */  
209 - float getFloat(const QString &key, float defaultValue) const; /*!< \brief Returns a float value for the key, returning \em defaultValue if the key does not exist. */  
210 - QString getString(const QString &key) const; /*!< \brief Returns a string value for the key, throwing an error if the key does not exist. */  
211 - QString getString(const QString &key, const QString &defaultValue) const; /*!< \brief Returns a string value for the key, returning \em defaultValue if the key does not exist. */  
212 -  
213 - QList<QPointF> landmarks() const; /*!< \brief Returns the file's landmark list. */  
214 - QList<QPointF> namedLandmarks() const; /*!< \brief Returns landmarks derived from metadata keys. */  
215 - void appendLandmark(const QPointF &landmark); /*!< \brief Adds a landmark to the file's landmark list. */  
216 - void appendLandmarks(const QList<QPointF> &landmarks); /*!< \brief Adds landmarks to the file's landmark list. */  
217 - inline void clearLandmarks() { m_metadata["Landmarks"] = QList<QVariant>(); } /*!< \brief Clears the file's landmark list. */  
218 - void setLandmarks(const QList<QPointF> &landmarks); /*!< \brief Assigns the file's landmark list. */  
219 -  
220 - QList<QRectF> ROIs() const; /*!< \brief Returns the file's ROI list. */  
221 - void appendROI(const QRectF &ROI); /*!< \brief Adds a ROI to the file's ROI list. */  
222 - void appendROIs(const QList<QRectF> &ROIs); /*!< \brief Adds ROIs to the file's ROI list. */  
223 - inline void clearROIs() { m_metadata["ROIs"] = QList<QVariant>(); } /*!< \brief Clears the file's landmark list. */  
224 - void setROIs(const QList<QRectF> &ROIs); /*!< \brief Assigns the file's landmark list. */ 223 + inline void setLabel(float label) { set("Label", label); } /*!< \brief Convenience function for setting the file's \c Label. */
  224 + inline bool failed() const { return get<bool>("FTE", false) || get<bool>("FTO", false); } /*!< \brief Returns \c true if the file failed to open or enroll, \c false otherwise. */
  225 +
  226 + QList<QPointF> namedPoints() const; /*!< \brief Returns points convertible from metadata keys. */
  227 + QList<QPointF> points() const; /*!< \brief Returns the file's points list. */
  228 + void appendPoint(const QPointF &point); /*!< \brief Adds a point to the file's point list. */
  229 + void appendPoints(const QList<QPointF> &points); /*!< \brief Adds landmarks to the file's landmark list. */
  230 + inline void clearPoints() { m_metadata["Points"] = QList<QVariant>(); } /*!< \brief Clears the file's landmark list. */
  231 + inline void setPoints(const QList<QPointF> &points) { clearPoints(); appendPoints(points); } /*!< \brief Overwrites the file's landmark list. */
  232 +
  233 + QList<QRectF> namedRects() const; /*!< \brief Returns rects convertible from metadata values. */
  234 + QList<QRectF> rects() const; /*!< \brief Returns the file's rects list. */
  235 + void appendRect(const QRectF &rect); /*!< \brief Adds a rect to the file's rect list. */
  236 + void appendRects(const QList<QRectF> &rects); /*!< \brief Adds rects to the file's rect list. */
  237 + inline void clearRects() { m_metadata["Rects"] = QList<QVariant>(); } /*!< \brief Clears the file's rect list. */
  238 + inline void setRects(const QList<QRectF> &rects) { clearRects(); appendRects(rects); } /*!< \brief Overwrites the file's rect list. */
225 239
226 private: 240 private:
227 - QHash<QString,QVariant> m_metadata;  
228 - BR_EXPORT friend QDataStream &operator<<(QDataStream &stream, const File &file); /*!< */  
229 - BR_EXPORT friend QDataStream &operator>>(QDataStream &stream, File &file); /*!< */ 241 + QMap<QString,QVariant> m_metadata;
  242 + BR_EXPORT friend QDataStream &operator<<(QDataStream &stream, const File &file);
  243 + BR_EXPORT friend QDataStream &operator>>(QDataStream &stream, File &file);
230 244
231 void init(const QString &file); 245 void init(const QString &file);
232 }; 246 };
@@ -416,6 +430,9 @@ class BR_EXPORT Object : public QObject @@ -416,6 +430,9 @@ class BR_EXPORT Object : public QObject
416 { 430 {
417 Q_OBJECT 431 Q_OBJECT
418 432
  433 + // Index of the first property that can be set via command line arguments
  434 + int firstAvailablePropertyIdx;
  435 +
419 public: 436 public:
420 File file; /*!< \brief The file used to construct the plugin. */ 437 File file; /*!< \brief The file used to construct the plugin. */
421 438
@@ -984,6 +1001,49 @@ public: @@ -984,6 +1001,49 @@ public:
984 virtual void backProject(const Template &dst, Template &src) const; /*!< \brief Invert the transform. */ 1001 virtual void backProject(const Template &dst, Template &src) const; /*!< \brief Invert the transform. */
985 virtual void backProject(const TemplateList &dst, TemplateList &src) const; /*!< \brief Invert the transform. */ 1002 virtual void backProject(const TemplateList &dst, TemplateList &src) const; /*!< \brief Invert the transform. */
986 1003
  1004 + /*!< \brief Apply the transform, may update the transform's internal state */
  1005 + virtual void projectUpdate(const Template &src, Template &dst)
  1006 + {
  1007 + project(src, dst);
  1008 + }
  1009 +
  1010 + /*!< \brief Apply the transform, may update the transform's internal state */
  1011 + virtual void projectUpdate(const TemplateList &src, TemplateList &dst)
  1012 + {
  1013 + project(src,dst);
  1014 + }
  1015 +
  1016 + /*!< \brief inplace projectUpdate. */
  1017 + void projectUpdate(Template &srcdst)
  1018 + {
  1019 + Template dst;
  1020 + projectUpdate(srcdst, dst);
  1021 + srcdst = dst;
  1022 + }
  1023 +
  1024 + /*!< \brief inplace projectUpdate. */
  1025 + void projectUpdate(TemplateList &srcdst)
  1026 + {
  1027 + TemplateList dst;
  1028 + projectUpdate(srcdst, dst);
  1029 + srcdst = dst;
  1030 + }
  1031 +
  1032 + /*!
  1033 + * Time-varying transforms may move away from a single input->single output model, and only emit
  1034 + * templates under some conditions (e.g. a tracking thing may emit a template for each detected
  1035 + * unique object), in this case finalize indicates that no further calls to project will be made
  1036 + * and the transform can emit a final set if templates if it wants. Time-invariant transforms
  1037 + * don't have to do anything.
  1038 + */
  1039 + virtual void finalize(TemplateList & output) { output = TemplateList(); }
  1040 +
  1041 + /*!
  1042 + * \brief Does the transform require the non-const version of project? Can vary for aggregation type transforms
  1043 + * (if their children are time varying, they are also time varying, otherwise probably not)
  1044 + */
  1045 + virtual bool timeVarying() const { return false; }
  1046 +
987 /*! 1047 /*!
988 * \brief Convenience function equivalent to project(). 1048 * \brief Convenience function equivalent to project().
989 */ 1049 */
@@ -1047,6 +1107,31 @@ inline QDataStream &amp;operator&gt;&gt;(QDataStream &amp;stream, Transform &amp;f) @@ -1047,6 +1107,31 @@ inline QDataStream &amp;operator&gt;&gt;(QDataStream &amp;stream, Transform &amp;f)
1047 } 1107 }
1048 1108
1049 /*! 1109 /*!
  1110 + * \brief A br::Transform for which the results of project may change due to prior calls to project
  1111 + */
  1112 +class BR_EXPORT TimeVaryingTransform : public Transform
  1113 +{
  1114 + Q_OBJECT
  1115 +
  1116 + virtual bool timeVarying() const { return true; }
  1117 +
  1118 + virtual void project(const Template &src, Template &dst) const
  1119 + {
  1120 + qFatal("No const project defined for time-varying transform");
  1121 + (void) dst; (void) src;
  1122 + }
  1123 +
  1124 + virtual void project(const TemplateList &src, TemplateList &dst) const
  1125 + {
  1126 + qFatal("No const project defined for time-varying transform");
  1127 + (void) dst; (void) src;
  1128 + }
  1129 +
  1130 +protected:
  1131 + TimeVaryingTransform(bool independent = true, bool trainable = true) : Transform(independent, trainable) {}
  1132 +};
  1133 +
  1134 +/*!
1050 * \brief A br::Transform expecting multiple matrices per template. 1135 * \brief A br::Transform expecting multiple matrices per template.
1051 */ 1136 */
1052 class BR_EXPORT MetaTransform : public Transform 1137 class BR_EXPORT MetaTransform : public Transform
@@ -1074,7 +1159,6 @@ private: @@ -1074,7 +1159,6 @@ private:
1074 void load(QDataStream &stream) { (void) stream; } 1159 void load(QDataStream &stream) { (void) stream; }
1075 }; 1160 };
1076 1161
1077 -  
1078 /*! 1162 /*!
1079 * \brief A br::MetaTransform that does not require training data. 1163 * \brief A br::MetaTransform that does not require training data.
1080 */ 1164 */
sdk/plugins/cascade.cpp
@@ -75,15 +75,15 @@ class CascadeTransform : public UntrainableTransform @@ -75,15 +75,15 @@ class CascadeTransform : public UntrainableTransform
75 { 75 {
76 CascadeClassifier *cascade = cascadeResource.acquire(); 76 CascadeClassifier *cascade = cascadeResource.acquire();
77 vector<Rect> rects; 77 vector<Rect> rects;
78 - cascade->detectMultiScale(src, rects, 1.2, 5, src.file.getBool("enrollAll") ? 0 : CV_HAAR_FIND_BIGGEST_OBJECT, Size(minSize, minSize)); 78 + cascade->detectMultiScale(src, rects, 1.2, 5, src.file.get<bool>("enrollAll", false) ? 0 : CV_HAAR_FIND_BIGGEST_OBJECT, Size(minSize, minSize));
79 cascadeResource.release(cascade); 79 cascadeResource.release(cascade);
80 80
81 - if (!src.file.getBool("enrollAll") && rects.empty()) 81 + if (!src.file.get<bool>("enrollAll", false) && rects.empty())
82 rects.push_back(Rect(0, 0, src.m().cols, src.m().rows)); 82 rects.push_back(Rect(0, 0, src.m().cols, src.m().rows));
83 83
84 foreach (const Rect &rect, rects) { 84 foreach (const Rect &rect, rects) {
85 dst += src; 85 dst += src;
86 - dst.file.appendROI(OpenCVUtils::fromRect(rect)); 86 + dst.file.appendRect(OpenCVUtils::fromRect(rect));
87 } 87 }
88 } 88 }
89 }; 89 };
sdk/plugins/crop.cpp
@@ -26,7 +26,7 @@ namespace br @@ -26,7 +26,7 @@ namespace br
26 26
27 /*! 27 /*!
28 * \ingroup transforms 28 * \ingroup transforms
29 - * \brief Crops the regions of interest. 29 + * \brief Crops the rectangular regions of interest.
30 * \author Josh Klontz \cite jklontz 30 * \author Josh Klontz \cite jklontz
31 */ 31 */
32 class ROITransform : public UntrainableTransform 32 class ROITransform : public UntrainableTransform
@@ -35,8 +35,8 @@ class ROITransform : public UntrainableTransform @@ -35,8 +35,8 @@ class ROITransform : public UntrainableTransform
35 35
36 void project(const Template &src, Template &dst) const 36 void project(const Template &src, Template &dst) const
37 { 37 {
38 - foreach (const QRectF ROI, src.file.ROIs())  
39 - dst += src.m()(OpenCVUtils::toRect(ROI)); 38 + foreach (const QRectF &rect, src.file.rects())
  39 + dst += src.m()(OpenCVUtils::toRect(rect));
40 } 40 }
41 }; 41 };
42 42
sdk/plugins/draw.cpp
@@ -47,19 +47,19 @@ class DrawTransform : public UntrainableTransform @@ -47,19 +47,19 @@ class DrawTransform : public UntrainableTransform
47 const Scalar verboseColor(255, 255, 0); 47 const Scalar verboseColor(255, 255, 0);
48 dst = src.m().clone(); 48 dst = src.m().clone();
49 49
50 - QList<Point2f> landmarks = OpenCVUtils::toPoints(src.file.landmarks()); 50 + QList<Point2f> landmarks = OpenCVUtils::toPoints(src.file.points());
51 51
52 if (unnamed) { 52 if (unnamed) {
53 foreach (const Point2f &landmark, landmarks) 53 foreach (const Point2f &landmark, landmarks)
54 circle(dst, landmark, 3, color, -1); 54 circle(dst, landmark, 3, color, -1);
55 } 55 }
56 if (named) { 56 if (named) {
57 - QList<Point2f> namedLandmarks = OpenCVUtils::toPoints(src.file.namedLandmarks()); 57 + QList<Point2f> namedLandmarks = OpenCVUtils::toPoints(src.file.namedPoints());
58 foreach (const Point2f &landmark, namedLandmarks) 58 foreach (const Point2f &landmark, namedLandmarks)
59 circle(dst, landmark, 3, color); 59 circle(dst, landmark, 3, color);
60 } 60 }
61 if (ROI) { 61 if (ROI) {
62 - QList<Rect> ROIs = OpenCVUtils::toRects(src.file.ROIs()); 62 + QList<Rect> ROIs = OpenCVUtils::toRects(src.file.rects());
63 foreach (const Rect ROI, ROIs) 63 foreach (const Rect ROI, ROIs)
64 rectangle(dst, ROI, color); 64 rectangle(dst, ROI, color);
65 } 65 }
@@ -154,11 +154,11 @@ class EditTransform : public UntrainableTransform @@ -154,11 +154,11 @@ class EditTransform : public UntrainableTransform
154 { 154 {
155 (void) event; 155 (void) event;
156 if (flags) { 156 if (flags) {
157 - QList<QRectF> ROIs = currentTemplate.file.ROIs();  
158 - for (int i=ROIs.size()-1; i>=0; i--)  
159 - if (ROIs[i].contains(x,y))  
160 - ROIs.removeAt(i);  
161 - currentTemplate.file.setROIs(ROIs); 157 + QList<QRectF> rects = currentTemplate.file.rects();
  158 + for (int i=rects.size()-1; i>=0; i--)
  159 + if (rects[i].contains(x,y))
  160 + rects.removeAt(i);
  161 + currentTemplate.file.setRects(rects);
162 } 162 }
163 163
164 Template temp; 164 Template temp;
sdk/plugins/eigen3.cpp
@@ -228,7 +228,7 @@ class DFFSTransform : public Transform @@ -228,7 +228,7 @@ class DFFSTransform : public Transform
228 void project(const Template &src, Template &dst) const 228 void project(const Template &src, Template &dst) const
229 { 229 {
230 dst = src; 230 dst = src;
231 - dst.file.insert("DFFS", sqrt(pca.residualReconstructionError((*cvtFloat)(src)))); 231 + dst.file.set("DFFS", sqrt(pca.residualReconstructionError((*cvtFloat)(src))));
232 } 232 }
233 233
234 void store(QDataStream &stream) const 234 void store(QDataStream &stream) const
sdk/plugins/eyes.cpp
@@ -147,7 +147,7 @@ public: @@ -147,7 +147,7 @@ public:
147 private: 147 private:
148 void project(const Template &src, Template &dst) const 148 void project(const Template &src, Template &dst) const
149 { 149 {
150 - Rect roi = OpenCVUtils::toRect(src.file.ROIs().first()); 150 + Rect roi = OpenCVUtils::toRect(src.file.rects().first());
151 151
152 Mat gray; 152 Mat gray;
153 OpenCVUtils::cvtGray(src.m()(roi), gray); 153 OpenCVUtils::cvtGray(src.m()(roi), gray);
@@ -183,12 +183,10 @@ private: @@ -183,12 +183,10 @@ private:
183 float second_eye_y = (right_rect.y + maxLoc.y)*gray.rows/height+roi.y; 183 float second_eye_y = (right_rect.y + maxLoc.y)*gray.rows/height+roi.y;
184 184
185 dst = src; 185 dst = src;
186 - dst.file.appendLandmark(QPointF(first_eye_x, first_eye_y));  
187 - dst.file.appendLandmark(QPointF(second_eye_x, second_eye_y));  
188 - dst.file.insert("ASEF_Right_Eye_X", first_eye_x);  
189 - dst.file.insert("ASEF_Right_Eye_Y", first_eye_y);  
190 - dst.file.insert("ASEF_Left_Eye_X", second_eye_x);  
191 - dst.file.insert("ASEF_Left_Eye_Y", second_eye_y); 186 + dst.file.appendPoint(QPointF(first_eye_x, first_eye_y));
  187 + dst.file.appendPoint(QPointF(second_eye_x, second_eye_y));
  188 + dst.file.set("ASEF_Right_Eye", QPointF(first_eye_x, first_eye_y));
  189 + dst.file.set("ASEF_Left_Eye", QPointF(second_eye_x, second_eye_y));
192 } 190 }
193 }; 191 };
194 192
sdk/plugins/format.cpp
@@ -217,11 +217,11 @@ class DefaultFormat : public Format @@ -217,11 +217,11 @@ class DefaultFormat : public Format
217 } else { 217 } else {
218 QString fileName = file.name; 218 QString fileName = file.name;
219 if (!QFileInfo(fileName).exists()) { 219 if (!QFileInfo(fileName).exists()) {
220 - fileName = file.getString("path") + "/" + file.name; 220 + fileName = file.get<QString>("path") + "/" + file.name;
221 if (!QFileInfo(fileName).exists()) { 221 if (!QFileInfo(fileName).exists()) {
222 fileName = file.fileName(); 222 fileName = file.fileName();
223 if (!QFileInfo(fileName).exists()) { 223 if (!QFileInfo(fileName).exists()) {
224 - fileName = file.getString("path") + "/" + file.fileName(); 224 + fileName = file.get<QString>("path") + "/" + file.fileName();
225 if (!QFileInfo(fileName).exists()) return t; 225 if (!QFileInfo(fileName).exists()) return t;
226 } 226 }
227 } 227 }
@@ -604,7 +604,7 @@ class xmlFormat : public Format @@ -604,7 +604,7 @@ class xmlFormat : public Format
604 (e.tagName() == "RPROFILE")) { 604 (e.tagName() == "RPROFILE")) {
605 // Ignore these other image fields for now 605 // Ignore these other image fields for now
606 } else { 606 } else {
607 - t.file.insert(e.tagName(), e.text()); 607 + t.file.set(e.tagName(), e.text());
608 } 608 }
609 609
610 fileNode = fileNode.nextSibling(); 610 fileNode = fileNode.nextSibling();
@@ -614,11 +614,11 @@ class xmlFormat : public Format @@ -614,11 +614,11 @@ class xmlFormat : public Format
614 614
615 // Calculate age 615 // Calculate age
616 if (t.file.contains("DOB")) { 616 if (t.file.contains("DOB")) {
617 - const QDate dob = QDate::fromString(t.file.getString("DOB").left(10), "yyyy-MM-dd"); 617 + const QDate dob = QDate::fromString(t.file.get<QString>("DOB").left(10), "yyyy-MM-dd");
618 const QDate current = QDate::currentDate(); 618 const QDate current = QDate::currentDate();
619 int age = current.year() - dob.year(); 619 int age = current.year() - dob.year();
620 if (current.month() < dob.month()) age--; 620 if (current.month() < dob.month()) age--;
621 - t.file.insert("Age", age); 621 + t.file.set("Age", age);
622 } 622 }
623 623
624 return t; 624 return t;
sdk/plugins/gallery.cpp
@@ -22,13 +22,13 @@ @@ -22,13 +22,13 @@
22 #include <QSqlQuery> 22 #include <QSqlQuery>
23 #include <QSqlRecord> 23 #include <QSqlRecord>
24 #endif // BR_EMBEDDED 24 #endif // BR_EMBEDDED
  25 +#include <opencv2/highgui/highgui.hpp>
25 #include <openbr_plugin.h> 26 #include <openbr_plugin.h>
26 27
27 #include "NaturalStringCompare.h" 28 #include "NaturalStringCompare.h"
28 #include "core/bee.h" 29 #include "core/bee.h"
29 #include "core/opencvutils.h" 30 #include "core/opencvutils.h"
30 #include "core/qtutils.h" 31 #include "core/qtutils.h"
31 -#include <opencv2/highgui/highgui.hpp>  
32 32
33 namespace br 33 namespace br
34 { 34 {
@@ -47,7 +47,7 @@ class galGallery : public Gallery @@ -47,7 +47,7 @@ class galGallery : public Gallery
47 void init() 47 void init()
48 { 48 {
49 gallery.setFileName(file); 49 gallery.setFileName(file);
50 - if (file.getBool("remove")) 50 + if (file.get<bool>("remove", false))
51 gallery.remove(); 51 gallery.remove();
52 QtUtils::touchDir(gallery); 52 QtUtils::touchDir(gallery);
53 if (!gallery.open(QFile::ReadWrite | QFile::Append)) 53 if (!gallery.open(QFile::ReadWrite | QFile::Append))
@@ -339,20 +339,33 @@ class csvGallery : public Gallery @@ -339,20 +339,33 @@ class csvGallery : public Gallery
339 { 339 {
340 if (files.isEmpty()) return; 340 if (files.isEmpty()) return;
341 341
342 - QStringList keys; 342 + QMap<QString,QVariant> samples;
343 foreach (const File &file, files) 343 foreach (const File &file, files)
344 foreach (const QString &key, file.localKeys()) 344 foreach (const QString &key, file.localKeys())
345 - if (!keys.contains(key)) keys += key;  
346 - qSort(keys); 345 + if (!samples.contains(key))
  346 + samples.insert(key, file.value(key));
  347 +
  348 + QStringList lines;
  349 + lines.reserve(files.size()+1);
  350 +
  351 + { // Make header
  352 + QStringList words;
  353 + words.append("File");
  354 + foreach (const QString &key, samples.keys())
  355 + words.append(getCSVElement(key, samples[key], true));
  356 + lines.append(words.join(","));
  357 + }
347 358
348 - const int rows = files.size();  
349 - const int columns = keys.size();  
350 - QSharedPointer<Output> output(Output::make(file, keys, files)); 359 + // Make table
  360 + foreach (const File &file, files) {
  361 + QStringList words;
  362 + words.append(file.name);
  363 + foreach (const QString &key, samples.keys())
  364 + words.append(getCSVElement(key, file.value(key), false));
  365 + lines.append(words.join(","));
  366 + }
351 367
352 - for (int i=0; i<rows; i++)  
353 - for (int j=0; j<columns; j++)  
354 - if (keys[j] == "Label") output->setRelative(files[i].label(), i, j);  
355 - else output->setRelative(files[i].getFloat(keys[j], std::numeric_limits<float>::quiet_NaN()), i, j); 368 + QtUtils::writeFile(file, lines);
356 } 369 }
357 370
358 TemplateList readBlock(bool *done) 371 TemplateList readBlock(bool *done)
@@ -377,6 +390,25 @@ class csvGallery : public Gallery @@ -377,6 +390,25 @@ class csvGallery : public Gallery
377 { 390 {
378 files.append(t.file); 391 files.append(t.file);
379 } 392 }
  393 +
  394 + static QString getCSVElement(const QString &key, const QVariant &value, bool header)
  395 + {
  396 + if (value.canConvert<QString>()) {
  397 + if (header) return key;
  398 + else return value.value<QString>();
  399 + } else if (value.canConvert<QPointF>()) {
  400 + const QPointF point = value.value<QPointF>();
  401 + if (header) return key+"_X,"+key+"_Y";
  402 + else return QString::number(point.x())+","+QString::number(point.y());
  403 + } else if (value.canConvert<QRectF>()) {
  404 + const QRectF rect = value.value<QRectF>();
  405 + if (header) return key+"_X,"+key+"_Y,"+key+"_Width,"+key+"_Height";
  406 + else return QString::number(rect.x())+","+QString::number(rect.y())+","+QString::number(rect.width())+","+QString::number(rect.height());
  407 + } else {
  408 + if (header) return key;
  409 + else return QString::number(std::numeric_limits<float>::quiet_NaN());
  410 + }
  411 + }
380 }; 412 };
381 413
382 BR_REGISTER(Gallery, csvGallery) 414 BR_REGISTER(Gallery, csvGallery)
@@ -465,9 +497,9 @@ class dbGallery : public Gallery @@ -465,9 +497,9 @@ class dbGallery : public Gallery
465 TemplateList readBlock(bool *done) 497 TemplateList readBlock(bool *done)
466 { 498 {
467 TemplateList templates; 499 TemplateList templates;
468 - br::File import = file.getString("import", "");  
469 - QString query = file.getString("query");  
470 - QString subset = file.getString("subset", ""); 500 + br::File import = file.get<QString>("import", "");
  501 + QString query = file.get<QString>("query");
  502 + QString subset = file.get<QString>("subset", "");
471 503
472 #ifndef BR_EMBEDDED 504 #ifndef BR_EMBEDDED
473 QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE"); 505 QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
sdk/plugins/hist.cpp
@@ -66,6 +66,38 @@ BR_REGISTER(Transform, HistTransform) @@ -66,6 +66,38 @@ BR_REGISTER(Transform, HistTransform)
66 66
67 /*! 67 /*!
68 * \ingroup transforms 68 * \ingroup transforms
  69 + * \brief Quantizes the values into bins.
  70 + * \author Josh Klontz \cite jklontz
  71 + */
  72 +class BinTransform : public UntrainableTransform
  73 +{
  74 + Q_OBJECT
  75 + Q_PROPERTY(float min READ get_min WRITE set_min RESET reset_min STORED false)
  76 + Q_PROPERTY(float max READ get_max WRITE set_max RESET reset_max STORED false)
  77 + Q_PROPERTY(int bins READ get_bins WRITE set_bins RESET reset_bins STORED false)
  78 + Q_PROPERTY(bool split READ get_split WRITE set_split RESET reset_split STORED false)
  79 + BR_PROPERTY(float, min, 0)
  80 + BR_PROPERTY(float, max, 255)
  81 + BR_PROPERTY(int, bins, 8)
  82 + BR_PROPERTY(bool, split, false)
  83 +
  84 + void project(const Template &src, Template &dst) const
  85 + {
  86 + src.m().convertTo(dst, bins > 256 ? CV_16U : CV_8U, bins/(max-min));
  87 + if (!split) return;
  88 +
  89 + Mat input = dst;
  90 + QList<Mat> outputs; outputs.reserve(bins);
  91 + for (int i=0; i<bins; i++)
  92 + outputs.append(input == i);
  93 + dst.clear(); dst.append(outputs);
  94 + }
  95 +};
  96 +
  97 +BR_REGISTER(Transform, BinTransform)
  98 +
  99 +/*!
  100 + * \ingroup transforms
69 * \brief Converts each element to its rank-ordered value. 101 * \brief Converts each element to its rank-ordered value.
70 * \author Josh Klontz \cite jklontz 102 * \author Josh Klontz \cite jklontz
71 */ 103 */
@@ -174,7 +206,7 @@ class VarianceChangeDetectorTransform : public UntrainableTransform @@ -174,7 +206,7 @@ class VarianceChangeDetectorTransform : public UntrainableTransform
174 int *buffer = new int[bins]; 206 int *buffer = new int[bins];
175 207
176 float bestRatio = -std::numeric_limits<float>::max(); 208 float bestRatio = -std::numeric_limits<float>::max();
177 - QRect bestROI; 209 + QRectF bestRect;
178 210
179 const int rows = m.rows; 211 const int rows = m.rows;
180 const int cols = m.cols/bins; 212 const int cols = m.cols/bins;
@@ -202,7 +234,7 @@ class VarianceChangeDetectorTransform : public UntrainableTransform @@ -202,7 +234,7 @@ class VarianceChangeDetectorTransform : public UntrainableTransform
202 234
203 if (ratio > bestRatio) { 235 if (ratio > bestRatio) {
204 bestRatio = ratio; 236 bestRatio = ratio;
205 - bestROI = QRect(j*radius, i*radius, scale*radius, scale*radius); 237 + bestRect = QRect(j*radius, i*radius, scale*radius, scale*radius);
206 } 238 }
207 } 239 }
208 } 240 }
@@ -210,7 +242,7 @@ class VarianceChangeDetectorTransform : public UntrainableTransform @@ -210,7 +242,7 @@ class VarianceChangeDetectorTransform : public UntrainableTransform
210 } 242 }
211 243
212 delete[] buffer; 244 delete[] buffer;
213 - dst.file.appendROI(bestROI); 245 + dst.file.appendRect(bestRect);
214 dst.file.setLabel(bestRatio); 246 dst.file.setLabel(bestRatio);
215 } 247 }
216 }; 248 };
sdk/plugins/integral.cpp 0 → 100644
  1 +#include <opencv2/imgproc/imgproc.hpp>
  2 +#include <openbr_plugin.h>
  3 +
  4 +namespace br
  5 +{
  6 +
  7 +/*!
  8 + * \ingroup transforms
  9 + * \brief Computes integral image.
  10 + * \author Josh Klontz \cite jklontz
  11 + */
  12 +class IntegralTransform : public UntrainableTransform
  13 +{
  14 + Q_OBJECT
  15 +
  16 + void project(const Template &src, Template &dst) const
  17 + {
  18 + cv::integral(src, dst);
  19 + }
  20 +};
  21 +
  22 +BR_REGISTER(Transform, IntegralTransform)
  23 +
  24 +/*!
  25 + * \ingroup transforms
  26 + * \brief Computes magnitude and/or angle of image.
  27 + * \author Josh Klontz \cite jklontz
  28 + */
  29 +class GradientTransform : public UntrainableTransform
  30 +{
  31 + Q_OBJECT
  32 + Q_ENUMS(Channel)
  33 + Q_PROPERTY(Channel channel READ get_channel WRITE set_channel RESET reset_channel STORED false)
  34 +
  35 +public:
  36 + enum Channel { Magnitude, Angle, MagnitudeAndAngle };
  37 +
  38 +private:
  39 + BR_PROPERTY(Channel, channel, Angle)
  40 +
  41 + void project(const Template &src, Template &dst) const
  42 + {
  43 + if (src.m().type() != CV_8UC1) qFatal("Requires CV_8UC1 input.");
  44 + cv::Mat dx, dy, magnitude, angle;
  45 + cv::Sobel(src, dx, CV_32F, 1, 0);
  46 + cv::Sobel(src, dy, CV_32F, 0, 1);
  47 + cv::cartToPolar(dx, dy, magnitude, angle, true);
  48 + if ((channel == Magnitude) || (channel == MagnitudeAndAngle))
  49 + dst.append(magnitude);
  50 + if ((channel == Angle) || (channel == MagnitudeAndAngle))
  51 + dst.append(angle);
  52 + }
  53 +};
  54 +
  55 +BR_REGISTER(Transform, GradientTransform)
  56 +
  57 +} // namespace br
  58 +
  59 +#include "integral.moc"
sdk/plugins/keypoint.cpp
@@ -54,13 +54,13 @@ class KeyPointDetectorTransform : public UntrainableTransform @@ -54,13 +54,13 @@ class KeyPointDetectorTransform : public UntrainableTransform
54 featureDetector->detect(src, keyPoints); 54 featureDetector->detect(src, keyPoints);
55 } catch (...) { 55 } catch (...) {
56 qWarning("Key point detection failed for file %s", qPrintable(src.file.name)); 56 qWarning("Key point detection failed for file %s", qPrintable(src.file.name));
57 - dst.file.setBool("FTE"); 57 + dst.file.set("FTE", true);
58 } 58 }
59 59
60 - QList<Rect> ROIs; 60 + QList<Rect> rects;
61 foreach (const KeyPoint &keyPoint, keyPoints) 61 foreach (const KeyPoint &keyPoint, keyPoints)
62 - ROIs.append(Rect(keyPoint.pt.x, keyPoint.pt.y, keyPoint.size, keyPoint.size));  
63 - dst.file.setROIs(OpenCVUtils::fromRects(ROIs)); 62 + rects.append(Rect(keyPoint.pt.x, keyPoint.pt.y, keyPoint.size, keyPoint.size));
  63 + dst.file.setRects(OpenCVUtils::fromRects(rects));
64 } 64 }
65 }; 65 };
66 66
@@ -92,10 +92,10 @@ class KeyPointDescriptorTransform : public UntrainableTransform @@ -92,10 +92,10 @@ class KeyPointDescriptorTransform : public UntrainableTransform
92 { 92 {
93 std::vector<KeyPoint> keyPoints; 93 std::vector<KeyPoint> keyPoints;
94 if (size == -1) { 94 if (size == -1) {
95 - foreach (const QRectF &ROI, src.file.ROIs()) 95 + foreach (const QRectF &ROI, src.file.rects())
96 keyPoints.push_back(KeyPoint(ROI.x(), ROI.y(), (ROI.width() + ROI.height())/2)); 96 keyPoints.push_back(KeyPoint(ROI.x(), ROI.y(), (ROI.width() + ROI.height())/2));
97 } else { 97 } else {
98 - foreach (const QPointF &landmark, src.file.landmarks()) 98 + foreach (const QPointF &landmark, src.file.points())
99 keyPoints.push_back(KeyPoint(landmark.x(), landmark.y(), size)); 99 keyPoints.push_back(KeyPoint(landmark.x(), landmark.y(), size));
100 } 100 }
101 descriptorExtractor->compute(src, keyPoints, dst); 101 descriptorExtractor->compute(src, keyPoints, dst);
@@ -166,7 +166,7 @@ class SIFTDescriptorTransform : public UntrainableTransform @@ -166,7 +166,7 @@ class SIFTDescriptorTransform : public UntrainableTransform
166 void project(const Template &src, Template &dst) const 166 void project(const Template &src, Template &dst) const
167 { 167 {
168 std::vector<KeyPoint> keyPoints; 168 std::vector<KeyPoint> keyPoints;
169 - foreach (const QPointF &val, src.file.landmarks()) 169 + foreach (const QPointF &val, src.file.points())
170 keyPoints.push_back(KeyPoint(val.x(), val.y(), size)); 170 keyPoints.push_back(KeyPoint(val.x(), val.y(), size));
171 171
172 Mat m; 172 Mat m;
@@ -200,7 +200,7 @@ class GridTransform : public UntrainableTransform @@ -200,7 +200,7 @@ class GridTransform : public UntrainableTransform
200 for (float j=column_step/2; j<src.m().cols; j+=column_step) 200 for (float j=column_step/2; j<src.m().cols; j+=column_step)
201 landmarks.append(QPointF(i,j)); 201 landmarks.append(QPointF(i,j));
202 dst = src; 202 dst = src;
203 - dst.file.setLandmarks(landmarks); 203 + dst.file.setPoints(landmarks);
204 } 204 }
205 }; 205 };
206 206
sdk/plugins/meta.cpp
@@ -31,24 +31,24 @@ static TemplateList Expanded(const TemplateList &amp;templates) @@ -31,24 +31,24 @@ static TemplateList Expanded(const TemplateList &amp;templates)
31 TemplateList expanded; 31 TemplateList expanded;
32 foreach (const Template &t, templates) { 32 foreach (const Template &t, templates) {
33 if (t.isEmpty()) { 33 if (t.isEmpty()) {
34 - if (!t.file.getBool("enrollAll")) 34 + if (!t.file.get<bool>("enrollAll", false))
35 expanded.append(t); 35 expanded.append(t);
36 continue; 36 continue;
37 } 37 }
38 38
39 - const bool fte = t.file.getBool("FTE");  
40 - QList<QPointF> landmarks = t.file.landmarks();  
41 - QList<QRectF> ROIs = t.file.ROIs();  
42 - if (landmarks.size() % t.size() != 0) qFatal("Uneven landmark count.");  
43 - if (ROIs.size() % t.size() != 0) qFatal("Uneven ROI count.");  
44 - const int landmarkStep = landmarks.size() / t.size();  
45 - const int ROIStep = ROIs.size() / t.size(); 39 + const bool fte = t.file.get<bool>("FTE", false);
  40 + QList<QPointF> points = t.file.points();
  41 + QList<QRectF> rects = t.file.rects();
  42 + if (points.size() % t.size() != 0) qFatal("Uneven point count.");
  43 + if (rects.size() % t.size() != 0) qFatal("Uneven rect count.");
  44 + const int pointStep = points.size() / t.size();
  45 + const int rectStep = rects.size() / t.size();
46 46
47 for (int i=0; i<t.size(); i++) { 47 for (int i=0; i<t.size(); i++) {
48 - if (!fte || !t.file.getBool("enrollAll")) { 48 + if (!fte || !t.file.get<bool>("enrollAll", false)) {
49 expanded.append(Template(t.file, t[i])); 49 expanded.append(Template(t.file, t[i]));
50 - expanded.last().file.setROIs(ROIs.mid(i*ROIStep, ROIStep));  
51 - expanded.last().file.setLandmarks(landmarks.mid(i*landmarkStep, landmarkStep)); 50 + expanded.last().file.setRects(rects.mid(i*rectStep, rectStep));
  51 + expanded.last().file.setPoints(points.mid(i*pointStep, pointStep));
52 } 52 }
53 } 53 }
54 } 54 }
@@ -86,6 +86,111 @@ static void incrementStep() @@ -86,6 +86,111 @@ static void incrementStep()
86 } 86 }
87 87
88 /*! 88 /*!
  89 + * \brief Use Expanded after basic calls that take a template list, used to implement ExpandTransform
  90 + */
  91 +class ExpandDecorator : public Transform
  92 +{
  93 + Q_OBJECT
  94 +
  95 + Q_PROPERTY(br::Transform* transform READ get_transform WRITE set_transform RESET reset_transform)
  96 + BR_PROPERTY(br::Transform*, transform, NULL)
  97 +
  98 +public:
  99 + ExpandDecorator(Transform * input)
  100 + {
  101 + transform = input;
  102 + transform->setParent(this);
  103 + file = transform->file;
  104 + setObjectName(transform->objectName());
  105 + }
  106 +
  107 + void train(const TemplateList &data)
  108 + {
  109 + transform->train(data);
  110 + }
  111 +
  112 + void project(const Template &src, Template &dst) const
  113 + {
  114 + transform->project(src, dst);
  115 + }
  116 +
  117 + void project(const TemplateList &src, TemplateList &dst) const
  118 + {
  119 + transform->project(src, dst);
  120 + dst = Expanded(dst);
  121 + }
  122 +
  123 +
  124 + void projectUpdate(const Template &src, Template &dst)
  125 + {
  126 + transform->projectUpdate(src, dst);
  127 + }
  128 +
  129 + void projectUpdate(const TemplateList & src, TemplateList & dst)
  130 + {
  131 + transform->projectUpdate(src, dst);
  132 + dst = Expanded(dst);
  133 + }
  134 +
  135 + bool timeVarying() const
  136 + {
  137 + return transform->timeVarying();
  138 + }
  139 +
  140 + void finalize(TemplateList & output)
  141 + {
  142 + transform->finalize(output);
  143 + output = Expanded(output);
  144 + }
  145 +
  146 +};
  147 +
  148 +/*!
  149 + * \brief A MetaTransform that aggregates some sub-transforms
  150 + */
  151 +class BR_EXPORT CompositeTransform : public TimeVaryingTransform
  152 +{
  153 + Q_OBJECT
  154 +
  155 +public:
  156 + Q_PROPERTY(QList<br::Transform*> transforms READ get_transforms WRITE set_transforms RESET reset_transforms)
  157 + BR_PROPERTY(QList<br::Transform*>, transforms, QList<br::Transform*>())
  158 +
  159 + virtual void project(const Template &src, Template &dst) const
  160 + {
  161 + if (timeVarying()) qFatal("No const project defined for time-varying transform");
  162 + _project(src, dst);
  163 + }
  164 +
  165 + virtual void project(const TemplateList &src, TemplateList &dst) const
  166 + {
  167 + if (timeVarying()) qFatal("No const project defined for time-varying transform");
  168 + _project(src, dst);
  169 + }
  170 +
  171 + bool timeVarying() const { return isTimeVarying; }
  172 +
  173 + void init()
  174 + {
  175 + isTimeVarying = false;
  176 + foreach (const br::Transform *transform, transforms) {
  177 + if (transform->timeVarying()) {
  178 + isTimeVarying = true;
  179 + break;
  180 + }
  181 + }
  182 + }
  183 +
  184 +protected:
  185 + bool isTimeVarying;
  186 +
  187 + virtual void _project(const Template & src, Template & dst) const = 0;
  188 + virtual void _project(const TemplateList & src, TemplateList & dst) const = 0;
  189 +
  190 + CompositeTransform() : TimeVaryingTransform(false) {}
  191 +};
  192 +
  193 +/*!
89 * \ingroup Transforms 194 * \ingroup Transforms
90 * \brief Transforms in series. 195 * \brief Transforms in series.
91 * \author Josh Klontz \cite jklontz 196 * \author Josh Klontz \cite jklontz
@@ -95,11 +200,9 @@ static void incrementStep() @@ -95,11 +200,9 @@ static void incrementStep()
95 * \see ExpandTransform 200 * \see ExpandTransform
96 * \see ForkTransform 201 * \see ForkTransform
97 */ 202 */
98 -class PipeTransform : public MetaTransform 203 +class PipeTransform : public CompositeTransform
99 { 204 {
100 Q_OBJECT 205 Q_OBJECT
101 - Q_PROPERTY(QList<br::Transform*> transforms READ get_transforms WRITE set_transforms RESET reset_transforms)  
102 - BR_PROPERTY(QList<br::Transform*>, transforms, QList<br::Transform*>())  
103 206
104 void train(const TemplateList &data) 207 void train(const TemplateList &data)
105 { 208 {
@@ -115,21 +218,78 @@ class PipeTransform : public MetaTransform @@ -115,21 +218,78 @@ class PipeTransform : public MetaTransform
115 releaseStep(); 218 releaseStep();
116 } 219 }
117 220
118 - void project(const Template &src, Template &dst) const 221 + void backProject(const Template &dst, Template &src) const
  222 + {
  223 + // Backprojecting a time-varying transform is probably not going to work.
  224 + if (timeVarying()) qFatal("No backProject defined for time-varying transform");
  225 +
  226 + src = dst;
  227 + // Reverse order in which transforms are processed
  228 + int length = transforms.length();
  229 + for (int i=length-1; i>=0; i--) {
  230 + Transform *f = transforms.at(i);
  231 + try {
  232 + src >> *f;
  233 + } catch (...) {
  234 + qWarning("Exception triggered when processing %s with transform %s", qPrintable(dst.file.flat()), qPrintable(f->objectName()));
  235 + src = Template(src.file);
  236 + src.file.set("FTE", true);
  237 + }
  238 + }
  239 + }
  240 +
  241 + void projectUpdate(const Template &src, Template &dst)
119 { 242 {
120 dst = src; 243 dst = src;
121 - foreach (const Transform *f, transforms) { 244 + foreach (Transform *f, transforms) {
122 try { 245 try {
123 - dst >> *f; 246 + f->projectUpdate(dst);
124 } catch (...) { 247 } catch (...) {
125 qWarning("Exception triggered when processing %s with transform %s", qPrintable(src.file.flat()), qPrintable(f->objectName())); 248 qWarning("Exception triggered when processing %s with transform %s", qPrintable(src.file.flat()), qPrintable(f->objectName()));
126 dst = Template(src.file); 249 dst = Template(src.file);
127 - dst.file.setBool("FTE"); 250 + dst.file.set("FTE", true);
128 } 251 }
129 } 252 }
130 } 253 }
131 254
132 - void project(const TemplateList &src, TemplateList &dst) const 255 + // For time varying transforms, parallel execution over individual templates
  256 + // won't work.
  257 + void projectUpdate(const TemplateList & src, TemplateList & dst)
  258 + {
  259 + dst = src;
  260 + foreach (Transform *f, transforms)
  261 + {
  262 + f->projectUpdate(dst);
  263 + }
  264 + }
  265 +
  266 + virtual void finalize(TemplateList & output)
  267 + {
  268 + output.clear();
  269 + // For each transform,
  270 + for (int i = 0; i < transforms.size(); i++)
  271 + {
  272 +
  273 + // Collect any final templates
  274 + TemplateList last_set;
  275 + transforms[i]->finalize(last_set);
  276 + if (last_set.empty())
  277 + continue;
  278 + // Push any templates received through the remaining transforms in the sequence
  279 + for (int j = (i+1); j < transforms.size();j++)
  280 + {
  281 + transforms[j]->projectUpdate(last_set);
  282 + }
  283 + // append the result to the output set
  284 + output.append(last_set);
  285 + }
  286 + }
  287 +
  288 +
  289 +protected:
  290 + // Template list project -- process templates in parallel through Transform::project
  291 + // or if parallelism is disabled, handle them sequentially
  292 + void _project(const TemplateList &src, TemplateList &dst) const
133 { 293 {
134 if (Globals->parallelism < 0) { 294 if (Globals->parallelism < 0) {
135 dst = src; 295 dst = src;
@@ -140,22 +300,20 @@ class PipeTransform : public MetaTransform @@ -140,22 +300,20 @@ class PipeTransform : public MetaTransform
140 } 300 }
141 } 301 }
142 302
143 - void backProject(const Template &dst, Template &src) const  
144 - {  
145 - src = dst;  
146 - // Reverse order in which transforms are processed  
147 - int length = transforms.length();  
148 - for (int i=length-1; i>=0; i--) {  
149 - Transform *f = transforms.at(i);  
150 - try {  
151 - src >> *f;  
152 - } catch (...) {  
153 - qWarning("Exception triggered when processing %s with transform %s", qPrintable(dst.file.flat()), qPrintable(f->objectName()));  
154 - src = Template(src.file);  
155 - src.file.setBool("FTE");  
156 - }  
157 - }  
158 - } 303 + // Single template const project, pass the template through each sub-transform, one after the other
  304 + virtual void _project(const Template & src, Template & dst) const
  305 + {
  306 + dst = src;
  307 + foreach (const Transform *f, transforms) {
  308 + try {
  309 + dst >> *f;
  310 + } catch (...) {
  311 + qWarning("Exception triggered when processing %s with transform %s", qPrintable(src.file.flat()), qPrintable(f->objectName()));
  312 + dst = Template(src.file);
  313 + dst.file.set("FTE", true);
  314 + }
  315 + }
  316 + }
159 }; 317 };
160 318
161 BR_REGISTER(Transform, PipeTransform) 319 BR_REGISTER(Transform, PipeTransform)
@@ -170,64 +328,31 @@ BR_REGISTER(Transform, PipeTransform) @@ -170,64 +328,31 @@ BR_REGISTER(Transform, PipeTransform)
170 * 328 *
171 * \see PipeTransform 329 * \see PipeTransform
172 */ 330 */
173 -class ExpandTransform : public MetaTransform 331 +class ExpandTransform : public PipeTransform
174 { 332 {
175 Q_OBJECT 333 Q_OBJECT
176 - Q_PROPERTY(QList<br::Transform*> transforms READ get_transforms WRITE set_transforms RESET reset_transforms)  
177 - BR_PROPERTY(QList<br::Transform*>, transforms, QList<br::Transform*>())  
178 334
179 - void train(const TemplateList &data) 335 + void init()
180 { 336 {
181 - acquireStep();  
182 -  
183 - TemplateList copy(data);  
184 - for (int i=0; i<transforms.size(); i++) {  
185 - transforms[i]->train(copy);  
186 - copy >> *transforms[i];  
187 - copy = Expanded(copy);  
188 - incrementStep(); 337 + for (int i = 0; i < transforms.size(); i++)
  338 + {
  339 + transforms[i] = new ExpandDecorator(transforms[i]);
189 } 340 }
190 -  
191 - releaseStep(); 341 + // Need to call this to set up timevariance correctly, and it won't
  342 + // be called automatically
  343 + CompositeTransform::init();
192 } 344 }
193 345
194 - void project(const Template &src, Template &dst) const  
195 - {  
196 - dst = src;  
197 - foreach (const Transform *f, transforms) {  
198 - try {  
199 - dst >> *f;  
200 - } catch (...) {  
201 - qWarning("Exception triggered when processing %s with transform %s", qPrintable(src.file.flat()), qPrintable(f->objectName()));  
202 - dst = Template(src.file);  
203 - dst.file.setBool("FTE");  
204 - }  
205 - }  
206 - } 346 +protected:
207 347
208 - void project(const TemplateList &src, TemplateList &dst) const 348 + // Template list project -- project through transforms sequentially,
  349 + // then expand the results, can't use Transform::Project(templateList) since
  350 + // we need to expand between tranforms, so actually do need to overload this method
  351 + void _project(const TemplateList &src, TemplateList &dst) const
209 { 352 {
210 dst = src; 353 dst = src;
211 for (int i=0; i<transforms.size(); i++) { 354 for (int i=0; i<transforms.size(); i++) {
212 dst >> *transforms[i]; 355 dst >> *transforms[i];
213 - dst = Expanded(dst);  
214 - }  
215 - }  
216 -  
217 - void backProject(const Template &dst, Template &src) const  
218 - {  
219 - src = dst;  
220 - // Reverse order in which transforms are processed  
221 - int length = transforms.length();  
222 - for (int i=length-1; i>=0; i--) {  
223 - Transform *f = transforms.at(i);  
224 - try {  
225 - src >> *f;  
226 - } catch (...) {  
227 - qWarning("Exception triggered when processing %s with transform %s", qPrintable(dst.file.flat()), qPrintable(f->objectName()));  
228 - src = Template(src.file);  
229 - src.file.setBool("FTE");  
230 - }  
231 } 356 }
232 } 357 }
233 }; 358 };
@@ -243,11 +368,9 @@ BR_REGISTER(Transform, ExpandTransform) @@ -243,11 +368,9 @@ BR_REGISTER(Transform, ExpandTransform)
243 * 368 *
244 * \see PipeTransform 369 * \see PipeTransform
245 */ 370 */
246 -class ForkTransform : public MetaTransform 371 +class ForkTransform : public CompositeTransform
247 { 372 {
248 Q_OBJECT 373 Q_OBJECT
249 - Q_PROPERTY(QList<br::Transform*> transforms READ get_transforms WRITE set_transforms RESET reset_transforms)  
250 - BR_PROPERTY(QList<br::Transform*>, transforms, QList<br::Transform*>())  
251 374
252 void train(const TemplateList &data) 375 void train(const TemplateList &data)
253 { 376 {
@@ -260,7 +383,69 @@ class ForkTransform : public MetaTransform @@ -260,7 +383,69 @@ class ForkTransform : public MetaTransform
260 if (threaded) Globals->trackFutures(futures); 383 if (threaded) Globals->trackFutures(futures);
261 } 384 }
262 385
263 - void project(const Template &src, Template &dst) const 386 + void backProject(const Template &dst, Template &src) const {Transform::backProject(dst, src);}
  387 +
  388 + // same as _project, but calls projectUpdate on sub-transforms
  389 + void projectupdate(const Template & src, Template & dst)
  390 + {
  391 + foreach (Transform *f, transforms) {
  392 + try {
  393 + Template res;
  394 + f->projectUpdate(src, res);
  395 + dst.merge(res);
  396 + } catch (...) {
  397 + qWarning("Exception triggered when processing %s with transform %s", qPrintable(src.file.flat()), qPrintable(f->objectName()));
  398 + dst = Template(src.file);
  399 + dst.file.set("FTE", true);
  400 + }
  401 + }
  402 + }
  403 +
  404 + void projectUpdate(const TemplateList & src, TemplateList & dst)
  405 + {
  406 + dst = src;
  407 + dst.reserve(src.size());
  408 + for (int i=0; i<src.size(); i++) dst.append(Template());
  409 + foreach (Transform *f, transforms) {
  410 + TemplateList m;
  411 + f->projectUpdate(src, m);
  412 + if (m.size() != dst.size()) qFatal("TemplateList is of an unexpected size.");
  413 + for (int i=0; i<src.size(); i++) dst[i].append(m[i]);
  414 + }
  415 + }
  416 +
  417 + // this is probably going to go bad, fork transform probably won't work well in a variable
  418 + // input/output scenario
  419 + virtual void finalize(TemplateList & output)
  420 + {
  421 + output.clear();
  422 + // For each transform,
  423 + for (int i = 0; i < transforms.size(); i++)
  424 + {
  425 + // Collect any final templates
  426 + TemplateList last_set;
  427 + transforms[i]->finalize(last_set);
  428 + if (last_set.empty())
  429 + continue;
  430 +
  431 + if (output.empty()) output = last_set;
  432 + else
  433 + {
  434 + // is the number of templates received from this transform consistent with the number
  435 + // received previously? If not we can't do anything coherent here.
  436 + if (last_set.size() != output.size())
  437 + qFatal("mismatched template list sizes in ForkTransform");
  438 + for (int j = 0; j < output.size(); j++) {
  439 + output[j].append(last_set[j]);
  440 + }
  441 + }
  442 + }
  443 + }
  444 +
  445 +protected:
  446 +
  447 + // Apply each transform to src, concatenate the results
  448 + void _project(const Template &src, Template &dst) const
264 { 449 {
265 foreach (const Transform *f, transforms) { 450 foreach (const Transform *f, transforms) {
266 try { 451 try {
@@ -268,12 +453,12 @@ class ForkTransform : public MetaTransform @@ -268,12 +453,12 @@ class ForkTransform : public MetaTransform
268 } catch (...) { 453 } catch (...) {
269 qWarning("Exception triggered when processing %s with transform %s", qPrintable(src.file.flat()), qPrintable(f->objectName())); 454 qWarning("Exception triggered when processing %s with transform %s", qPrintable(src.file.flat()), qPrintable(f->objectName()));
270 dst = Template(src.file); 455 dst = Template(src.file);
271 - dst.file.setBool("FTE"); 456 + dst.file.set("FTE", true);
272 } 457 }
273 } 458 }
274 } 459 }
275 460
276 - void project(const TemplateList &src, TemplateList &dst) const 461 + void _project(const TemplateList &src, TemplateList &dst) const
277 { 462 {
278 if (Globals->parallelism < 0) { 463 if (Globals->parallelism < 0) {
279 dst.reserve(src.size()); 464 dst.reserve(src.size());
@@ -288,6 +473,7 @@ class ForkTransform : public MetaTransform @@ -288,6 +473,7 @@ class ForkTransform : public MetaTransform
288 Transform::project(src, dst); 473 Transform::project(src, dst);
289 } 474 }
290 } 475 }
  476 +
291 }; 477 };
292 478
293 BR_REGISTER(Transform, ForkTransform) 479 BR_REGISTER(Transform, ForkTransform)
@@ -423,7 +609,7 @@ private: @@ -423,7 +609,7 @@ private:
423 const QString file = getFileName(); 609 const QString file = getFileName();
424 if (file.isEmpty()) return false; 610 if (file.isEmpty()) return false;
425 611
426 - qDebug("Loading %s", qPrintable(baseName)); 612 + if (Globals->verbose) qDebug("Loading %s", qPrintable(baseName));
427 QByteArray data; 613 QByteArray data;
428 QtUtils::readFile(file, data, true); 614 QtUtils::readFile(file, data, true);
429 QDataStream stream(&data, QFile::ReadOnly); 615 QDataStream stream(&data, QFile::ReadOnly);
@@ -462,7 +648,7 @@ class FTETransform : public Transform @@ -462,7 +648,7 @@ class FTETransform : public Transform
462 foreach (const Template &t, projectedData) { 648 foreach (const Template &t, projectedData) {
463 if (!t.file.contains(transform->objectName())) 649 if (!t.file.contains(transform->objectName()))
464 qFatal("Matrix metadata missing key %s.", qPrintable(transform->objectName())); 650 qFatal("Matrix metadata missing key %s.", qPrintable(transform->objectName()));
465 - vals.append(t.file.getFloat(transform->objectName())); 651 + vals.append(t.file.get<float>(transform->objectName()));
466 } 652 }
467 float q1, q3; 653 float q1, q3;
468 Common::Median(vals, &q1, &q3); 654 Common::Median(vals, &q1, &q3);
@@ -474,11 +660,11 @@ class FTETransform : public Transform @@ -474,11 +660,11 @@ class FTETransform : public Transform
474 { 660 {
475 Template projectedSrc; 661 Template projectedSrc;
476 transform->project(src, projectedSrc); 662 transform->project(src, projectedSrc);
477 - const float val = projectedSrc.file.getFloat(transform->objectName()); 663 + const float val = projectedSrc.file.get<float>(transform->objectName());
478 664
479 dst = src; 665 dst = src;
480 - dst.file.insert(transform->objectName(), val);  
481 - dst.file.insert("FTE", (val < min) || (val > max)); 666 + dst.file.set(transform->objectName(), val);
  667 + dst.file.set("FTE", (val < min) || (val > max));
482 } 668 }
483 }; 669 };
484 670
sdk/plugins/misc.cpp
@@ -44,7 +44,7 @@ class OpenTransform : public UntrainableMetaTransform @@ -44,7 +44,7 @@ class OpenTransform : public UntrainableMetaTransform
44 dst.append(t); 44 dst.append(t);
45 dst.file.append(t.file.localMetadata()); 45 dst.file.append(t.file.localMetadata());
46 } 46 }
47 - dst.file.insert("FTO", dst.isEmpty()); 47 + dst.file.set("FTO", dst.isEmpty());
48 } 48 }
49 }; 49 };
50 50
@@ -100,16 +100,19 @@ class PrintTransform : public UntrainableMetaTransform @@ -100,16 +100,19 @@ class PrintTransform : public UntrainableMetaTransform
100 Q_OBJECT 100 Q_OBJECT
101 Q_PROPERTY(bool error READ get_error WRITE set_error RESET reset_error) 101 Q_PROPERTY(bool error READ get_error WRITE set_error RESET reset_error)
102 Q_PROPERTY(bool data READ get_data WRITE set_data RESET reset_data) 102 Q_PROPERTY(bool data READ get_data WRITE set_data RESET reset_data)
  103 + Q_PROPERTY(bool nTemplates READ get_data WRITE set_data RESET reset_data)
103 BR_PROPERTY(bool, error, true) 104 BR_PROPERTY(bool, error, true)
104 BR_PROPERTY(bool, data, false) 105 BR_PROPERTY(bool, data, false)
  106 + BR_PROPERTY(bool, size, false)
105 107
106 void project(const Template &src, Template &dst) const 108 void project(const Template &src, Template &dst) const
107 { 109 {
108 dst = src; 110 dst = src;
109 const QString nameString = src.file.flat(); 111 const QString nameString = src.file.flat();
110 const QString dataString = data ? OpenCVUtils::matrixToString(src)+"\n" : QString(); 112 const QString dataString = data ? OpenCVUtils::matrixToString(src)+"\n" : QString();
111 - if (error) qDebug("%s\n%s", qPrintable(nameString), qPrintable(dataString));  
112 - else printf("%s\n%s", qPrintable(nameString), qPrintable(dataString)); 113 + const QString nTemplates = size ? QString::number(src.size()) : QString();
  114 + if (error) qDebug("%s\n%s\n%s", qPrintable(nameString), qPrintable(dataString), qPrintable(nTemplates));
  115 + else printf("%s\n%s\n%s", qPrintable(nameString), qPrintable(dataString), qPrintable(nTemplates));
113 } 116 }
114 }; 117 };
115 118
@@ -282,7 +285,7 @@ class RenameTransform : public UntrainableMetaTransform @@ -282,7 +285,7 @@ class RenameTransform : public UntrainableMetaTransform
282 { 285 {
283 dst = src; 286 dst = src;
284 if (dst.file.localKeys().contains(find)) { 287 if (dst.file.localKeys().contains(find)) {
285 - dst.file.insert(replace, dst.file.get(find)); 288 + dst.file.set(replace, dst.file.value(find));
286 dst.file.remove(find); 289 dst.file.remove(find);
287 } 290 }
288 } 291 }
@@ -308,7 +311,7 @@ class RenameFirstTransform : public UntrainableMetaTransform @@ -308,7 +311,7 @@ class RenameFirstTransform : public UntrainableMetaTransform
308 dst = src; 311 dst = src;
309 foreach (const QString &key, find) 312 foreach (const QString &key, find)
310 if (dst.file.localKeys().contains(key)) { 313 if (dst.file.localKeys().contains(key)) {
311 - dst.file.insert(replace, dst.file.get(key)); 314 + dst.file.set(replace, dst.file.value(key));
312 dst.file.remove(key); 315 dst.file.remove(key);
313 break; 316 break;
314 } 317 }
sdk/plugins/output.cpp
@@ -106,7 +106,7 @@ class meltOutput : public MatrixOutput @@ -106,7 +106,7 @@ class meltOutput : public MatrixOutput
106 const bool genuineOnly = file.contains("Genuine") && !file.contains("Impostor"); 106 const bool genuineOnly = file.contains("Genuine") && !file.contains("Impostor");
107 const bool impostorOnly = file.contains("Impostor") && !file.contains("Genuine"); 107 const bool impostorOnly = file.contains("Impostor") && !file.contains("Genuine");
108 108
109 - QHash<QString,QVariant> args = file.localMetadata(); 109 + QMap<QString,QVariant> args = file.localMetadata();
110 args.remove("Genuine"); 110 args.remove("Genuine");
111 args.remove("Impostor"); 111 args.remove("Impostor");
112 112
@@ -165,9 +165,9 @@ class rrOutput : public MatrixOutput @@ -165,9 +165,9 @@ class rrOutput : public MatrixOutput
165 ~rrOutput() 165 ~rrOutput()
166 { 166 {
167 if (file.isNull() || targetFiles.isEmpty() || queryFiles.isEmpty()) return; 167 if (file.isNull() || targetFiles.isEmpty() || queryFiles.isEmpty()) return;
168 - const int limit = file.getInt("limit", 20);  
169 - const bool byLine = file.getBool("byLine");  
170 - const float threshold = file.getFloat("threshold", -std::numeric_limits<float>::max()); 168 + const int limit = file.get<int>("limit", 20);
  169 + const bool byLine = file.get<bool>("byLine", false);
  170 + const float threshold = file.get<float>("threshold", -std::numeric_limits<float>::max());
171 171
172 QStringList lines; 172 QStringList lines;
173 173
@@ -279,7 +279,7 @@ class rankOutput : public MatrixOutput @@ -279,7 +279,7 @@ class rankOutput : public MatrixOutput
279 typedef QPair<float,int> Pair; 279 typedef QPair<float,int> Pair;
280 int rank = 1; 280 int rank = 1;
281 foreach (const Pair &pair, Common::Sort(OpenCVUtils::matrixToVector(data.row(i)), true)) { 281 foreach (const Pair &pair, Common::Sort(OpenCVUtils::matrixToVector(data.row(i)), true)) {
282 - if(targetFiles[pair.second].getString("Label") == queryFiles[i].getString("Label")) { 282 + if(targetFiles[pair.second].get<QString>("Label") == queryFiles[i].get<QString>("Label")) {
283 ranks.append(rank); 283 ranks.append(rank);
284 positions.append(pair.second); 284 positions.append(pair.second);
285 scores.append(pair.first); 285 scores.append(pair.first);
@@ -347,10 +347,10 @@ class tailOutput : public Output @@ -347,10 +347,10 @@ class tailOutput : public Output
347 void initialize(const FileList &targetFiles, const FileList &queryFiles) 347 void initialize(const FileList &targetFiles, const FileList &queryFiles)
348 { 348 {
349 Output::initialize(targetFiles, queryFiles); 349 Output::initialize(targetFiles, queryFiles);
350 - threshold = file.getFloat("threshold", -std::numeric_limits<float>::max());  
351 - atLeast = file.getInt("atLeast", 1);  
352 - atMost = file.getInt("atMost", std::numeric_limits<int>::max());  
353 - args = file.getBool("args"); 350 + threshold = file.get<float>("threshold", -std::numeric_limits<float>::max());
  351 + atLeast = file.get<int>("atLeast", 1);
  352 + atMost = file.get<int>("atMost", std::numeric_limits<int>::max());
  353 + args = file.get<bool>("args", false);
354 lastValue = -std::numeric_limits<float>::max(); 354 lastValue = -std::numeric_limits<float>::max();
355 } 355 }
356 356
@@ -462,9 +462,9 @@ class histOutput : public Output @@ -462,9 +462,9 @@ class histOutput : public Output
462 void initialize(const FileList &targetFiles, const FileList &queryFiles) 462 void initialize(const FileList &targetFiles, const FileList &queryFiles)
463 { 463 {
464 Output::initialize(targetFiles, queryFiles); 464 Output::initialize(targetFiles, queryFiles);
465 - min = file.getFloat("min", -5);  
466 - max = file.getFloat("max", 5);  
467 - step = file.getFloat("step", 0.1); 465 + min = file.get<float>("min", -5);
  466 + max = file.get<float>("max", 5);
  467 + step = file.get<float>("step", 0.1);
468 bins = QVector<int>((max-min)/step, 0); 468 bins = QVector<int>((max-min)/step, 0);
469 } 469 }
470 470
sdk/plugins/pixel.cpp
@@ -120,7 +120,7 @@ class PerPixelClassifierTransform : public MetaTransform @@ -120,7 +120,7 @@ class PerPixelClassifierTransform : public MetaTransform
120 uchar *psrc = src[n].ptr(); 120 uchar *psrc = src[n].ptr();
121 ptemp[n] = psrc[index]; 121 ptemp[n] = psrc[index];
122 } 122 }
123 - cv::Mat labelMat = src.file.get("labels").value<cv::Mat>(); 123 + cv::Mat labelMat = src.file.value("labels").value<cv::Mat>();
124 uchar* plabel = labelMat.ptr(); 124 uchar* plabel = labelMat.ptr();
125 temp.file.setLabel(plabel[index]); 125 temp.file.setLabel(plabel[index]);
126 126
sdk/plugins/pp5.cpp
@@ -148,9 +148,9 @@ struct PP5Context @@ -148,9 +148,9 @@ struct PP5Context
148 return "Unknown"; 148 return "Unknown";
149 } 149 }
150 150
151 - static QHash<QString,QVariant> toMetadata(const ppr_face_type &face) 151 + static QMap<QString,QVariant> toMetadata(const ppr_face_type &face)
152 { 152 {
153 - QHash<QString,QVariant> metadata; 153 + QMap<QString,QVariant> metadata;
154 154
155 ppr_face_attributes_type face_attributes; 155 ppr_face_attributes_type face_attributes;
156 ppr_get_face_attributes(face, &face_attributes); 156 ppr_get_face_attributes(face, &face_attributes);
@@ -250,7 +250,7 @@ class PP5Enroll : public UntrainableTransform @@ -250,7 +250,7 @@ class PP5Enroll : public UntrainableTransform
250 dst.file.append(PP5Context::toMetadata(face)); 250 dst.file.append(PP5Context::toMetadata(face));
251 dst += m; 251 dst += m;
252 252
253 - if (!src.file.getBool("enrollAll")) break; 253 + if (!src.file.get<bool>("enrollAll", false)) break;
254 } 254 }
255 255
256 ppr_free_face_list(face_list); 256 ppr_free_face_list(face_list);
@@ -259,7 +259,7 @@ class PP5Enroll : public UntrainableTransform @@ -259,7 +259,7 @@ class PP5Enroll : public UntrainableTransform
259 259
260 contexts.release(context); 260 contexts.release(context);
261 261
262 - if (!src.file.getBool("enrollAll") && dst.isEmpty()) { 262 + if (!src.file.get<bool>("enrollAll", false) && dst.isEmpty()) {
263 if (detectOnly) dst += src; 263 if (detectOnly) dst += src;
264 else dst += cv::Mat(); 264 else dst += cv::Mat();
265 } 265 }
sdk/plugins/quality.cpp
@@ -53,8 +53,8 @@ class ImpostorUniquenessMeasureTransform : public Transform @@ -53,8 +53,8 @@ class ImpostorUniquenessMeasureTransform : public Transform
53 { 53 {
54 dst = src; 54 dst = src;
55 float ium = calculateIUM(src, impostors); 55 float ium = calculateIUM(src, impostors);
56 - dst.file.insert("Impostor_Uniqueness_Measure", ium);  
57 - dst.file.insert("Impostor_Uniqueness_Measure_Bin", ium < mean-stddev ? 0 : (ium < mean+stddev ? 1 : 2)); 56 + dst.file.set("Impostor_Uniqueness_Measure", ium);
  57 + dst.file.set("Impostor_Uniqueness_Measure_Bin", ium < mean-stddev ? 0 : (ium < mean+stddev ? 1 : 2));
58 } 58 }
59 59
60 void store(QDataStream &stream) const 60 void store(QDataStream &stream) const
sdk/plugins/random.cpp
@@ -27,47 +27,6 @@ namespace br @@ -27,47 +27,6 @@ namespace br
27 27
28 /*! 28 /*!
29 * \ingroup transforms 29 * \ingroup transforms
30 - * \brief Selects a random transform.  
31 - * \author Josh Klontz \cite jklontz  
32 - */  
33 -class RndTransformTransform : public Transform  
34 -{  
35 - Q_OBJECT  
36 - Q_PROPERTY(QList<br::Transform*> transforms READ get_transforms WRITE set_transforms RESET reset_transforms STORED false)  
37 - BR_PROPERTY(QList<br::Transform*>, transforms, QList<br::Transform*>())  
38 -  
39 - int selectedIndex;  
40 - Transform *selectedTransform;  
41 -  
42 - void train(const TemplateList &data)  
43 - {  
44 - selectedIndex = theRNG().uniform(0, transforms.size());  
45 - selectedTransform = transforms[selectedIndex]->clone();  
46 - selectedTransform->train(data);  
47 - }  
48 -  
49 - void project(const Template &src, Template &dst) const  
50 - {  
51 - selectedTransform->project(src, dst);  
52 - }  
53 -  
54 - void store(QDataStream &stream) const  
55 - {  
56 - stream << selectedIndex << *selectedTransform;  
57 - }  
58 -  
59 - void load(QDataStream &stream)  
60 - {  
61 - stream >> selectedIndex;  
62 - selectedTransform = transforms[selectedIndex]->clone();  
63 - stream >> *selectedTransform;  
64 - }  
65 -};  
66 -  
67 -BR_REGISTER(Transform, RndTransformTransform)  
68 -  
69 -/*!  
70 - * \ingroup transforms  
71 * \brief Generates a random subspace. 30 * \brief Generates a random subspace.
72 * \author Josh Klontz \cite jklontz 31 * \author Josh Klontz \cite jklontz
73 */ 32 */
@@ -196,7 +155,7 @@ class RndPointTransform : public Transform @@ -196,7 +155,7 @@ class RndPointTransform : public Transform
196 void project(const Template &src, Template &dst) const 155 void project(const Template &src, Template &dst) const
197 { 156 {
198 dst = src; 157 dst = src;
199 - dst.file.appendLandmark(QPointF(src.m().cols * x, src.m().rows * y)); 158 + dst.file.appendPoint(QPointF(src.m().cols * x, src.m().rows * y));
200 } 159 }
201 }; 160 };
202 161
sdk/plugins/regions.cpp
@@ -148,8 +148,8 @@ class RectFromLandmarksTransform : public UntrainableTransform @@ -148,8 +148,8 @@ class RectFromLandmarksTransform : public UntrainableTransform
148 148
149 void project(const Template &src, Template &dst) const 149 void project(const Template &src, Template &dst) const
150 { 150 {
151 - if (src.file.landmarks().isEmpty()) {  
152 - qWarning("No landmarks for %s", qPrintable(src.file.fileName())); 151 + if (src.file.points().isEmpty()) {
  152 + qWarning("No landmarks");
153 dst = src; 153 dst = src;
154 return; 154 return;
155 } 155 }
@@ -160,12 +160,12 @@ class RectFromLandmarksTransform : public UntrainableTransform @@ -160,12 +160,12 @@ class RectFromLandmarksTransform : public UntrainableTransform
160 maxX = maxY = -std::numeric_limits<int>::max(); 160 maxX = maxY = -std::numeric_limits<int>::max();
161 161
162 foreach(int index, indices) { 162 foreach(int index, indices) {
163 - if (src.file.landmarks().size() > index+1) {  
164 - if (src.file.landmarks()[index].x() < minX) minX = src.file.landmarks()[index].x();  
165 - if (src.file.landmarks()[index].x() > maxX) maxX = src.file.landmarks()[index].x();  
166 - if (src.file.landmarks()[index].y() < minY) minY = src.file.landmarks()[index].y();  
167 - if (src.file.landmarks()[index].y() > maxY) maxY = src.file.landmarks()[index].y();  
168 - dst.file.appendLandmark(src.file.landmarks()[index]); 163 + if (src.file.points().size() > index+1) {
  164 + if (src.file.points()[index].x() < minX) minX = src.file.points()[index].x();
  165 + if (src.file.points()[index].x() > maxX) maxX = src.file.points()[index].x();
  166 + if (src.file.points()[index].y() < minY) minY = src.file.points()[index].y();
  167 + if (src.file.points()[index].y() > maxY) maxY = src.file.points()[index].y();
  168 + dst.file.appendPoint(src.file.points()[index]);
169 } 169 }
170 } 170 }
171 171
sdk/plugins/register.cpp
@@ -67,17 +67,14 @@ class AffineTransform : public UntrainableTransform @@ -67,17 +67,14 @@ class AffineTransform : public UntrainableTransform
67 else dstPoints[2] = Point2f(x3*width, y3*height); 67 else dstPoints[2] = Point2f(x3*width, y3*height);
68 68
69 Point2f srcPoints[3]; 69 Point2f srcPoints[3];
70 - if (src.file.contains("Affine_0_X") &&  
71 - src.file.contains("Affine_0_Y") &&  
72 - src.file.contains("Affine_1_X") &&  
73 - src.file.contains("Affine_1_Y") &&  
74 - (src.file.contains("Affine_2_X") || twoPoints) &&  
75 - (src.file.contains("Affine_2_Y") || twoPoints)) {  
76 - srcPoints[0] = Point2f(src.file.getFloat("Affine_0_X"), src.file.getFloat("Affine_0_Y"));  
77 - srcPoints[1] = Point2f(src.file.getFloat("Affine_1_X"), src.file.getFloat("Affine_1_Y"));  
78 - if (!twoPoints) srcPoints[2] = Point2f(src.file.getFloat("Affine_2_X"), src.file.getFloat("Affine_2_Y")); 70 + if (src.file.contains("Affine_0") &&
  71 + src.file.contains("Affine_1") &&
  72 + (src.file.contains("Affine_2") || twoPoints)) {
  73 + srcPoints[0] = OpenCVUtils::toPoint(src.file.get<QPointF>("Affine_0"));
  74 + srcPoints[1] = OpenCVUtils::toPoint(src.file.get<QPointF>("Affine_1"));
  75 + if (!twoPoints) srcPoints[2] = OpenCVUtils::toPoint(src.file.get<QPointF>("Affine_2"));
79 } else { 76 } else {
80 - const QList<Point2f> landmarks = OpenCVUtils::toPoints(src.file.landmarks()); 77 + const QList<Point2f> landmarks = OpenCVUtils::toPoints(src.file.points());
81 78
82 if ((landmarks.size() < 2) || (!twoPoints && (landmarks.size() < 3))) { 79 if ((landmarks.size() < 2) || (!twoPoints && (landmarks.size() < 3))) {
83 resize(src, dst, Size(width, height)); 80 resize(src, dst, Size(width, height));
@@ -87,14 +84,9 @@ class AffineTransform : public UntrainableTransform @@ -87,14 +84,9 @@ class AffineTransform : public UntrainableTransform
87 srcPoints[1] = landmarks[1]; 84 srcPoints[1] = landmarks[1];
88 if (!twoPoints) srcPoints[2] = landmarks[2]; 85 if (!twoPoints) srcPoints[2] = landmarks[2];
89 86
90 - dst.file.insert("Affine_0_X", landmarks[0].x);  
91 - dst.file.insert("Affine_0_Y", landmarks[0].y);  
92 - dst.file.insert("Affine_1_X", landmarks[1].x);  
93 - dst.file.insert("Affine_1_Y", landmarks[1].y);  
94 - if (!twoPoints) {  
95 - dst.file.insert("Affine_2_X", landmarks[2].x);  
96 - dst.file.insert("Affine_2_Y", landmarks[2].y);  
97 - } 87 + dst.file.set("Affine_0", OpenCVUtils::fromPoint(landmarks[0]));
  88 + dst.file.set("Affine_1", OpenCVUtils::fromPoint(landmarks[1]));
  89 + if (!twoPoints) dst.file.set("Affine_2", OpenCVUtils::fromPoint(landmarks[2]));
98 } 90 }
99 } 91 }
100 if (twoPoints) srcPoints[2] = getThirdAffinePoint(srcPoints[0], srcPoints[1]); 92 if (twoPoints) srcPoints[2] = getThirdAffinePoint(srcPoints[0], srcPoints[1]);
sdk/plugins/stasm.cpp
@@ -18,7 +18,7 @@ class StasmInitializer : public Initializer @@ -18,7 +18,7 @@ class StasmInitializer : public Initializer
18 18
19 void initialize() const 19 void initialize() const
20 { 20 {
21 - Globals->abbreviations.insert("RectFromStasmEyes","RectFromLandmarks([27, 28, 29, 30, 31, 32, 33, 34, 35, 36],0.125,6.0)+Resize(44,168)"); // 21 + Globals->abbreviations.insert("RectFromStasmEyes","RectFromLandmarks([27, 28, 29, 30, 31, 32, 33, 34, 35, 36],0.125,6.0)+Resize(44,164)"); //
22 Globals->abbreviations.insert("RectFromStasmJaw","RectFromLandmarks([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14],10)"); 22 Globals->abbreviations.insert("RectFromStasmJaw","RectFromLandmarks([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14],10)");
23 Globals->abbreviations.insert("RectFromStasmBrow","RectFromLandmarks([15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26],0.25,6.5)+Resize(44,230)"); 23 Globals->abbreviations.insert("RectFromStasmBrow","RectFromLandmarks([15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26],0.25,6.5)+Resize(44,230)");
24 Globals->abbreviations.insert("RectFromStasmNose","RectFromLandmarks([38, 39, 40, 41, 42, 43, 44, 67],0.1,1.5)+Resize(44,44)"); 24 Globals->abbreviations.insert("RectFromStasmNose","RectFromLandmarks([38, 39, 40, 41, 42, 43, 44, 67],0.1,1.5)+Resize(44,44)");
@@ -54,13 +54,13 @@ class StasmTransform : public UntrainableTransform @@ -54,13 +54,13 @@ class StasmTransform : public UntrainableTransform
54 54
55 if (nlandmarks == 0) { 55 if (nlandmarks == 0) {
56 qWarning("Unable to detect Stasm landmarks for %s", qPrintable(src.file.fileName())); 56 qWarning("Unable to detect Stasm landmarks for %s", qPrintable(src.file.fileName()));
57 - dst.file.setBool("FTE"); 57 + dst.file.set("FTE", true);
58 dst.m() = src.m(); 58 dst.m() = src.m();
59 return; 59 return;
60 } 60 }
61 61
62 for (int i = 0; i < nlandmarks; i++) 62 for (int i = 0; i < nlandmarks; i++)
63 - dst.file.appendLandmark(QPointF(landmarks[2 * i], landmarks[2 * i + 1])); 63 + dst.file.appendPoint(QPointF(landmarks[2 * i], landmarks[2 * i + 1]));
64 64
65 dst.m() = src.m(); 65 dst.m() = src.m();
66 } 66 }
sdk/plugins/validate.cpp
@@ -22,7 +22,7 @@ class CrossValidateTransform : public MetaTransform @@ -22,7 +22,7 @@ class CrossValidateTransform : public MetaTransform
22 int numPartitions = 0; 22 int numPartitions = 0;
23 QList<int> partitions; partitions.reserve(data.size()); 23 QList<int> partitions; partitions.reserve(data.size());
24 foreach (const File &file, data.files()) { 24 foreach (const File &file, data.files()) {
25 - partitions.append(file.getInt("Cross_Validation_Partition", 0)); 25 + partitions.append(file.get<int>("Cross_Validation_Partition", 0));
26 numPartitions = std::max(numPartitions, partitions.last()+1); 26 numPartitions = std::max(numPartitions, partitions.last()+1);
27 } 27 }
28 28
@@ -48,7 +48,7 @@ class CrossValidateTransform : public MetaTransform @@ -48,7 +48,7 @@ class CrossValidateTransform : public MetaTransform
48 48
49 void project(const Template &src, Template &dst) const 49 void project(const Template &src, Template &dst) const
50 { 50 {
51 - transforms[src.file.getInt("Cross_Validation_Partition", 0)]->project(src, dst); 51 + transforms[src.file.get<int>("Cross_Validation_Partition", 0)]->project(src, dst);
52 } 52 }
53 53
54 void store(QDataStream &stream) const 54 void store(QDataStream &stream) const
@@ -82,8 +82,8 @@ class CrossValidateDistance : public Distance @@ -82,8 +82,8 @@ class CrossValidateDistance : public Distance
82 82
83 float compare(const Template &a, const Template &b) const 83 float compare(const Template &a, const Template &b) const
84 { 84 {
85 - const int partitionA = a.file.getInt("Cross_Validation_Partition", 0);  
86 - const int partitionB = b.file.getInt("Cross_Validation_Partition", 0); 85 + const int partitionA = a.file.get<int>("Cross_Validation_Partition", 0);
  86 + const int partitionB = b.file.get<int>("Cross_Validation_Partition", 0);
87 return (partitionA != partitionB) ? -std::numeric_limits<float>::max() : 0; 87 return (partitionA != partitionB) ? -std::numeric_limits<float>::max() : 0;
88 } 88 }
89 }; 89 };
@@ -104,7 +104,7 @@ class FilterDistance : public Distance @@ -104,7 +104,7 @@ class FilterDistance : public Distance
104 (void) b; // Query template isn't checked 104 (void) b; // Query template isn't checked
105 foreach (const QString &key, Globals->filters.keys()) { 105 foreach (const QString &key, Globals->filters.keys()) {
106 bool keep = false; 106 bool keep = false;
107 - const QString metadata = a.file.getString(key, ""); 107 + const QString metadata = a.file.get<QString>(key, "");
108 if (metadata.isEmpty()) continue; 108 if (metadata.isEmpty()) continue;
109 foreach (const QString &value, Globals->filters[key]) { 109 foreach (const QString &value, Globals->filters[key]) {
110 if (metadata == value) { 110 if (metadata == value) {
sdk/plugins/wavelet.cpp
@@ -189,11 +189,11 @@ class GaborJetTransform : public UntrainableTransform @@ -189,11 +189,11 @@ class GaborJetTransform : public UntrainableTransform
189 189
190 void project(const Template &src, Template &dst) const 190 void project(const Template &src, Template &dst) const
191 { 191 {
192 - const QList<QPointF> landmarks = src.file.landmarks();  
193 - dst = Mat(landmarks.size(), kReals.size(), CV_32FC1);  
194 - for (int i=0; i<landmarks.size(); i++) 192 + const QList<QPointF> points = src.file.points();
  193 + dst = Mat(points.size(), kReals.size(), CV_32FC1);
  194 + for (int i=0; i<points.size(); i++)
195 for (int j=0; j<kReals.size(); j++) 195 for (int j=0; j<kReals.size(); j++)
196 - dst.m().at<float>(i,j) = response(src, landmarks[i], kReals[j], kImaginaries[j], component); 196 + dst.m().at<float>(i,j) = response(src, points[i], kReals[j], kImaginaries[j], component);
197 } 197 }
198 }; 198 };
199 199
sdk/plugins/youtube.cpp
1 #include <openbr_plugin.h> 1 #include <openbr_plugin.h>
2 2
  3 +#include "core/common.h"
  4 +
3 namespace br 5 namespace br
4 { 6 {
5 7
@@ -19,24 +21,27 @@ class YouTubeFacesDBTransform : public UntrainableMetaTransform @@ -19,24 +21,27 @@ class YouTubeFacesDBTransform : public UntrainableMetaTransform
19 21
20 void init() 22 void init()
21 { 23 {
  24 + if (algorithm.isEmpty()) return;
22 transform = Transform::fromAlgorithm(algorithm); 25 transform = Transform::fromAlgorithm(algorithm);
23 distance = Distance::fromAlgorithm(algorithm); 26 distance = Distance::fromAlgorithm(algorithm);
24 } 27 }
25 28
26 - void project(const Template &src, Template &dst) const 29 + void project(const TemplateList &src, TemplateList &dst) const
27 { 30 {
28 - dst = src;  
29 -  
30 - // First input is the header in 'splits.txt'  
31 - if (src.file.getInt("Index") == 0) return; 31 + Transform::project(src.mid(1) /* First template is the header in 'splits.txt' */, dst);
  32 + }
32 33
  34 + void project(const Template &src, Template &dst) const
  35 + {
33 const QStringList words = src.file.name.split(", "); 36 const QStringList words = src.file.name.split(", ");
34 dst.file.name = words[0] + "_" + words[1] + "_" + words[4] + ".mtx"; 37 dst.file.name = words[0] + "_" + words[1] + "_" + words[4] + ".mtx";
35 38
36 TemplateList queryTemplates = TemplateList::fromGallery(File(words[2]).resolved()); 39 TemplateList queryTemplates = TemplateList::fromGallery(File(words[2]).resolved());
  40 + sort(queryTemplates);
37 queryTemplates >> *transform; 41 queryTemplates >> *transform;
38 42
39 TemplateList targetTemplates = TemplateList::fromGallery(File(words[3]).resolved()); 43 TemplateList targetTemplates = TemplateList::fromGallery(File(words[3]).resolved());
  44 + sort(targetTemplates);
40 targetTemplates >> *transform; 45 targetTemplates >> *transform;
41 46
42 QScopedPointer<MatrixOutput> memoryOutput(MatrixOutput::make(targetTemplates.files(), queryTemplates.files())); 47 QScopedPointer<MatrixOutput> memoryOutput(MatrixOutput::make(targetTemplates.files(), queryTemplates.files()));
@@ -45,6 +50,26 @@ class YouTubeFacesDBTransform : public UntrainableMetaTransform @@ -45,6 +50,26 @@ class YouTubeFacesDBTransform : public UntrainableMetaTransform
45 dst.clear(); 50 dst.clear();
46 dst.m() = memoryOutput.data()->data; 51 dst.m() = memoryOutput.data()->data;
47 } 52 }
  53 +
  54 + static void sort(TemplateList &templates)
  55 + {
  56 + // The file names in the YouTube Faces Database make it very difficult
  57 + // for them to be ordered by frame number automatically,
  58 + // hence we do it manually here.
  59 + QList<int> frames;
  60 + foreach (const Template &t, templates) {
  61 + QStringList words = t.file.name.split('.');
  62 + frames.append(words[words.size()-2].toInt());
  63 + }
  64 +
  65 + typedef QPair<int,int> SortedFrame; // <frame number, original index>
  66 + QList<SortedFrame> sortedFrames = Common::Sort(frames);
  67 + TemplateList sortedTemplates; sortedTemplates.reserve(templates.size());
  68 + foreach (const SortedFrame &sortedFrame, sortedFrames)
  69 + sortedTemplates.append(templates[sortedFrame.second]);
  70 +
  71 + templates = sortedTemplates;
  72 + }
48 }; 73 };
49 74
50 BR_REGISTER(Transform, YouTubeFacesDBTransform) 75 BR_REGISTER(Transform, YouTubeFacesDBTransform)