Commit 2ca8d5156d59890ca49d427238ed3ce118a28bd5

Authored by M Taborsky
2 parents d48d3a56 88cbcc4f

Merge branch 'master' of https://github.com/biometrics/openbr

sdk/core/bee.cpp
... ... @@ -47,6 +47,9 @@ FileList BEE::readSigset(QString sigset, bool ignoreMetadata)
47 47 file.close();
48 48  
49 49 QDomElement docElem = doc.documentElement();
  50 + if (docElem.nodeName() != "biometric-signature-set")
  51 + return fileList;
  52 +
50 53 QDomNode subject = docElem.firstChild();
51 54 while (!subject.isNull()) {
52 55 // Looping through subjects
... ...
sdk/core/plot.cpp
... ... @@ -431,7 +431,7 @@ bool br::Plot(const QStringList &files, const QString &destination, bool show)
431 431 p.file.write(qPrintable(QString("qplot(X, Y, data=DET, geom=\"line\"") +
432 432 (p.majorSize > 1 ? QString(", colour=factor(%1)").arg(p.majorHeader) : QString()) +
433 433 (p.minorSize > 1 ? QString(", linetype=factor(%1)").arg(p.minorHeader) : QString()) +
434   - QString(", xlab=\"False Accept Rate\", ylab=\"False Reject Rate\") + geom_abline(alpha=0.5, colour=\"grey\", linetype=\"dashed\") + theme_bw()") +
  434 + QString(", xlab=\"False Accept Rate\", ylab=\"False Reject Rate\") + geom_abline(alpha=0.5, colour=\"grey\", linetype=\"dashed\") + theme_minimal()") +
435 435 (p.majorSize > 1 ? getScale("colour", p.majorHeader, p.majorSize) : QString()) +
436 436 (p.minorSize > 1 ? QString(" + scale_linetype_discrete(\"%1\")").arg(p.minorHeader) : QString()) +
437 437 QString(" + scale_x_continuous(trans=\"log10\") + scale_y_continuous(trans=\"log10\")") +
... ... @@ -440,7 +440,7 @@ bool br::Plot(const QStringList &files, const QString &destination, bool show)
440 440 p.file.write(qPrintable(QString("qplot(X, 1-Y, data=DET, geom=\"line\"") +
441 441 (p.majorSize > 1 ? QString(", colour=factor(%1)").arg(p.majorHeader) : QString()) +
442 442 (p.minorSize > 1 ? QString(", linetype=factor(%1)").arg(p.minorHeader) : QString()) +
443   - QString(", xlab=\"False Accept Rate\", ylab=\"True Accept Rate\") + theme_bw()") +
  443 + QString(", xlab=\"False Accept Rate\", ylab=\"True Accept Rate\") + theme_minimal()") +
444 444 (p.majorSize > 1 ? getScale("colour", p.majorHeader, p.majorSize) : QString()) +
445 445 (p.minorSize > 1 ? QString(" + scale_linetype_discrete(\"%1\")").arg(p.minorHeader) : QString()) +
446 446 QString(" + scale_x_continuous(trans=\"log10\") + scale_y_continuous(labels=percent)") +
... ... @@ -449,15 +449,15 @@ bool br::Plot(const QStringList &files, const QString &destination, bool show)
449 449 p.file.write(qPrintable(QString("qplot(X, data=SD, geom=\"histogram\", fill=Y, position=\"identity\", alpha=I(1/2)") +
450 450 QString(", xlab=\"Score%1\"").arg((p.flip ? p.majorSize : p.minorSize) > 1 ? " / " + (p.flip ? p.majorHeader : p.minorHeader) : QString()) +
451 451 QString(", ylab=\"Frequency%1\"").arg((p.flip ? p.minorSize : p.majorSize) > 1 ? " / " + (p.flip ? p.minorHeader : p.majorHeader) : QString()) +
452   - QString(") + scale_fill_manual(\"Ground Truth\", values=c(\"blue\", \"red\")) + theme_bw() + scale_x_continuous(minor_breaks=NULL) + scale_y_continuous(minor_breaks=NULL) + opts(axis.text.y=theme_blank(), axis.ticks=theme_blank(), axis.text.x=theme_text(angle=-90, hjust=0))") +
  452 + QString(") + scale_fill_manual(\"Ground Truth\", values=c(\"blue\", \"red\")) + theme_minimal() + scale_x_continuous(minor_breaks=NULL) + scale_y_continuous(minor_breaks=NULL) + theme(axis.text.y=element_blank(), axis.ticks=element_blank(), axis.text.x=element_text(angle=-90, hjust=0))") +
453 453 (p.majorSize > 1 ? (p.minorSize > 1 ? QString(" + facet_grid(%2 ~ %1, scales=\"free\")").arg((p.flip ? p.majorHeader : p.minorHeader), (p.flip ? p.minorHeader : p.majorHeader)) : QString(" + facet_wrap(~ %1, scales = \"free\")").arg(p.majorHeader)) : QString()) +
454   - QString(" + opts(aspect.ratio=1)") +
  454 + QString(" + theme(aspect.ratio=1)") +
455 455 QString("\nggsave(\"%1\")\n").arg(p.subfile("SD"))));
456 456  
457 457 p.file.write(qPrintable(QString("qplot(X, Y, data=CMC, geom=\"line\", xlab=\"Rank\", ylab=\"Retrieval Rate\"") +
458 458 (p.majorSize > 1 ? QString(", colour=factor(%1)").arg(p.majorHeader) : QString()) +
459 459 (p.minorSize > 1 ? QString(", linetype=factor(%1)").arg(p.minorHeader) : QString()) +
460   - QString(") + theme_bw() + scale_x_continuous(limits = c(1,25), breaks = c(1,5,10,25))") +
  460 + QString(") + theme_minimal() + scale_x_continuous(limits = c(1,25), breaks = c(1,5,10,25))") +
461 461 (p.majorSize > 1 ? getScale("colour", p.majorHeader, p.majorSize) : QString()) +
462 462 (p.minorSize > 1 ? QString(" + scale_linetype_discrete(\"%1\")").arg(p.minorHeader) : QString()) +
463 463 QString(" + scale_y_continuous(labels=percent)") +
... ... @@ -466,28 +466,28 @@ bool br::Plot(const QStringList &files, const QString &destination, bool show)
466 466 p.file.write(qPrintable(QString("qplot(factor(%1), data=BC, geom=\"bar\", position=\"dodge\", weight=Y").arg(p.majorHeader) +
467 467 (p.majorSize > 1 ? QString(", fill=factor(%1)").arg(p.majorHeader) : QString()) +
468 468 QString(", xlab=\"%1False Accept Rate\"").arg(p.majorSize > 1 ? p.majorHeader + " / " : QString()) +
469   - QString(", ylab=\"True Accept Rate%1\") + theme_bw()").arg(p.minorSize > 1 ? " / " + p.minorHeader : QString()) +
  469 + QString(", ylab=\"True Accept Rate%1\") + theme_minimal()").arg(p.minorSize > 1 ? " / " + p.minorHeader : QString()) +
470 470 (p.majorSize > 1 ? getScale("fill", p.majorHeader, p.majorSize) : QString()) +
471 471 (p.minorSize > 1 ? QString(" + facet_grid(%2 ~ X)").arg(p.minorHeader) : QString(" + facet_wrap(~ X)")) +
472   - QString(" + opts(legend.position=\"none\", axis.text.x=theme_text(angle=-90, hjust=0)) + geom_text(data=BC, aes(label=Y, y=0.05))") +
  472 + QString(" + theme(legend.position=\"none\", axis.text.x=element_text(angle=-90, hjust=0)) + geom_text(data=BC, aes(label=Y, y=0.05))") +
473 473 QString("\nggsave(\"%1\")\n").arg(p.subfile("BC"))));
474 474  
475 475 p.file.write(qPrintable(QString("qplot(X, Y, data=FAR, geom=\"line\"") +
476 476 ((p.flip ? p.majorSize : p.minorSize) > 1 ? QString(", colour=factor(%1)").arg(p.flip ? p.majorHeader : p.minorHeader) : QString()) +
477   - QString(", xlab=\"Score%1\", ylab=\"False Accept Rate\") + theme_bw()").arg((p.flip ? p.minorSize : p.majorSize) > 1 ? " / " + (p.flip ? p.minorHeader : p.majorHeader) : QString()) +
  477 + QString(", xlab=\"Score%1\", ylab=\"False Accept Rate\") + theme_minimal()").arg((p.flip ? p.minorSize : p.majorSize) > 1 ? " / " + (p.flip ? p.minorHeader : p.majorHeader) : QString()) +
478 478 ((p.flip ? p.majorSize : p.minorSize) > 1 ? getScale("colour", p.flip ? p.majorHeader : p.minorHeader, p.flip ? p.majorSize : p.minorSize) : QString()) +
479 479 QString(" + scale_y_continuous(trans=\"log10\")") +
480 480 ((p.flip ? p.minorSize : p.majorSize) > 1 ? QString(" + facet_wrap(~ %1, scales=\"free_x\")").arg(p.flip ? p.minorHeader : p.majorHeader) : QString()) +
481   - QString(" + opts(aspect.ratio=1)") +
  481 + QString(" + theme(aspect.ratio=1)") +
482 482 QString("\nggsave(\"%1\")\n").arg(p.subfile("FAR"))));
483 483  
484 484 p.file.write(qPrintable(QString("qplot(X, Y, data=FRR, geom=\"line\"") +
485 485 ((p.flip ? p.majorSize : p.minorSize) > 1 ? QString(", colour=factor(%1)").arg(p.flip ? p.majorHeader : p.minorHeader) : QString()) +
486   - QString(", xlab=\"Score%1\", ylab=\"False Reject Rate\") + theme_bw()").arg((p.flip ? p.minorSize : p.majorSize) > 1 ? " / " + (p.flip ? p.minorHeader : p.majorHeader) : QString()) +
  486 + QString(", xlab=\"Score%1\", ylab=\"False Reject Rate\") + theme_minimal()").arg((p.flip ? p.minorSize : p.majorSize) > 1 ? " / " + (p.flip ? p.minorHeader : p.majorHeader) : QString()) +
487 487 ((p.flip ? p.majorSize : p.minorSize) > 1 ? getScale("colour", p.flip ? p.majorHeader : p.minorHeader, p.flip ? p.majorSize : p.minorSize) : QString()) +
488 488 QString(" + scale_y_continuous(trans=\"log10\")") +
489 489 ((p.flip ? p.minorSize : p.majorSize) > 1 ? QString(" + facet_wrap(~ %1, scales=\"free_x\")").arg(p.flip ? p.minorHeader : p.majorHeader) : QString()) +
490   - QString(" + opts(aspect.ratio=1)") +
  490 + QString(" + theme(aspect.ratio=1)") +
491 491 QString("\nggsave(\"%1\")\n").arg(p.subfile("FRR"))));
492 492  
493 493 return p.finalize(show);
... ... @@ -499,6 +499,6 @@ bool br::PlotMetadata(const QStringList &files, const QString &columns, bool sho
499 499  
500 500 RPlot p(files, "PlotMetadata", false);
501 501 foreach (const QString &column, columns.split(";"))
502   - p.file.write(qPrintable(QString("qplot(%1, %2, data=data, geom=\"violin\", fill=%1) + coord_flip() + theme_bw()\nggsave(\"%2.pdf\")\n").arg(p.majorHeader, column)));
  502 + p.file.write(qPrintable(QString("qplot(%1, %2, data=data, geom=\"violin\", fill=%1) + coord_flip() + theme_minimal()\nggsave(\"%2.pdf\")\n").arg(p.majorHeader, column)));
503 503 return p.finalize(show);
504 504 }
... ...
sdk/openbr_plugin.cpp
... ... @@ -112,13 +112,12 @@ float File::label() const
112 112 const QVariant variant = value("Label");
113 113 if (variant.isNull()) return -1;
114 114  
115   - if (variant.canConvert(QVariant::Double)) {
116   - bool ok;
117   - float val = variant.toFloat(&ok);
118   - if (ok) return val;
119   - }
  115 + if (Globals->classes.contains(variant.toString()))
  116 + return Globals->classes.value(variant.toString());
120 117  
121   - return Globals->classes.value(variant.toString(), -1);
  118 + bool ok;
  119 + const float val = variant.toFloat(&ok);
  120 + return ok ? val : -1;
122 121 }
123 122  
124 123 void File::remove(const QString &key)
... ... @@ -130,10 +129,17 @@ void File::set(const QString &key, const QVariant &value)
130 129 {
131 130 if (key == "Label") {
132 131 bool ok = false;
133   - if (value.canConvert(QVariant::Double))
  132 + const QString valueString = value.toString();
  133 +
  134 + /* We assume that if the value starts with '0'
  135 + then it was probably intended to to be a string UID
  136 + and that it's numerical value is not relevant. */
  137 + if (value.canConvert(QVariant::Double) &&
  138 + (!valueString.startsWith('0') || (valueString == "0")))
134 139 value.toFloat(&ok);
135   - if (!ok && !Globals->classes.contains(value.toString()))
136   - Globals->classes.insert(value.toString(), Globals->classes.size());
  140 +
  141 + if (!ok && !Globals->classes.contains(valueString))
  142 + Globals->classes.insert(valueString, Globals->classes.size());
137 143 }
138 144  
139 145 m_metadata.insert(key, value);
... ... @@ -377,6 +383,10 @@ TemplateList TemplateList::fromInput(const br::File &input)
377 383 QScopedPointer<Gallery> i(Gallery::make(file));
378 384 TemplateList newTemplates = i->read();
379 385  
  386 + // If input is a Format not a Gallery
  387 + if (newTemplates.isEmpty())
  388 + newTemplates.append(input);
  389 +
380 390 // Propogate metadata
381 391 for (int i=0; i<newTemplates.size(); i++) {
382 392 newTemplates[i].file.append(input.localMetadata());
... ... @@ -451,6 +461,8 @@ QString Object::argument(int index) const
451 461 return "[" + strings.join(",") + "]";
452 462 } else if (type == "br::Transform*") {
453 463 return variant.value<Transform*>()->description();
  464 + } else if (type == "QStringList") {
  465 + return "[" + variant.toStringList().join(",") + "]";
454 466 }
455 467  
456 468 return variant.toString();
... ... @@ -484,6 +496,10 @@ void Object::store(QDataStream &amp;stream) const
484 496 stream << property.read(this).toFloat();
485 497 } else if (type == "double") {
486 498 stream << property.read(this).toDouble();
  499 + } else if (type == "QString") {
  500 + stream << property.read(this).toString();
  501 + } else if (type == "QStringList") {
  502 + stream << property.read(this).toStringList();
487 503 } else {
488 504 qFatal("Can't serialize value of type: %s", qPrintable(type));
489 505 }
... ... @@ -520,6 +536,14 @@ void Object::load(QDataStream &amp;stream)
520 536 double value;
521 537 stream >> value;
522 538 property.write(this, value);
  539 + } else if (type == "QString") {
  540 + QString value;
  541 + stream >> value;
  542 + property.write(this, value);
  543 + } else if (type == "QStringList") {
  544 + QStringList value;
  545 + stream >> value;
  546 + property.write(this, value);
523 547 } else {
524 548 qFatal("Can't serialize value of type: %s", qPrintable(type));
525 549 }
... ... @@ -560,6 +584,8 @@ void Object::setProperty(const QString &amp;name, const QString &amp;value)
560 584 }
561 585 } else if (type == "br::Transform*") {
562 586 variant.setValue(Transform::make(value, this));
  587 + } else if (type == "QStringList") {
  588 + variant.setValue(parse(value.mid(1, value.size()-2)));
563 589 } else if (type == "bool") {
564 590 if (value.isEmpty()) variant = true;
565 591 else if (value == "false") variant = false;
... ... @@ -1218,6 +1244,29 @@ void Distance::compare(const TemplateList &amp;target, const TemplateList &amp;query, Ou
1218 1244 if (Globals->parallelism) Globals->trackFutures(futures);
1219 1245 }
1220 1246  
  1247 +float Distance::compare(const Template &target, const Template &query) const
  1248 +{
  1249 + if (!Globals->demographicFilters.isEmpty()) {
  1250 + // The if statement is a faster check then iterating over an empty list of filters
  1251 + foreach (const QString &filter, Globals->demographicFilters) {
  1252 + const QString targetMetadata = target.file.getString(filter, "");
  1253 + const QString queryMetadata = query.file.getString(filter, "");
  1254 + if (targetMetadata.isEmpty() || queryMetadata.isEmpty()) continue;
  1255 + if (targetMetadata != queryMetadata) return -std::numeric_limits<float>::max();
  1256 + }
  1257 + }
  1258 +
  1259 + if (Globals->ageDelta < std::numeric_limits<float>::max()) {
  1260 + const float targetAge = target.file.getFloat("Age", -1);
  1261 + const float queryAge = target.file.getFloat("Age", -1);
  1262 + if ((targetAge != -1) && (queryAge != -1) && (abs(targetAge - queryAge) > Globals->ageDelta))
  1263 + return -std::numeric_limits<float>::max();
  1264 + }
  1265 +
  1266 + return a * (_compare(target, query) - b);
  1267 +}
  1268 +
  1269 +/* Distance - private methods */
1221 1270 void Distance::compareBlock(const TemplateList &target, const TemplateList &query, Output *output, int targetOffset, int queryOffset) const
1222 1271 {
1223 1272 for (int i=0; i<query.size(); i++)
... ...
sdk/openbr_plugin.h
... ... @@ -485,6 +485,18 @@ public:
485 485 Q_PROPERTY(bool enrollAll READ get_enrollAll WRITE set_enrollAll RESET reset_enrollAll)
486 486 BR_PROPERTY(bool, enrollAll, false)
487 487  
  488 + /*!
  489 + * \brief Keys to use when matching templates to automatically determine non-match based on template metadata.
  490 + */
  491 + Q_PROPERTY(QStringList demographicFilters READ get_demographicFilters WRITE set_demographicFilters RESET reset_demographicFilters)
  492 + BR_PROPERTY(QStringList, demographicFilters, QStringList())
  493 +
  494 + /*!
  495 + * \brief Allowable age difference when matching templates.
  496 + */
  497 + Q_PROPERTY(float ageDelta READ get_ageDelta WRITE set_ageDelta RESET reset_ageDelta)
  498 + BR_PROPERTY(float, ageDelta, std::numeric_limits<float>::max())
  499 +
488 500 QHash<QString,QString> abbreviations; /*!< \brief Used by br::Transform::make() to expand abbreviated algorithms into their complete definitions. */
489 501 QHash<QString,int> classes; /*!< \brief Used by classifiers to associate text class labels with unique integers IDs. */
490 502 QTime startTime; /*!< \brief Used to estimate timeRemaining(). */
... ... @@ -821,9 +833,9 @@ private:
821 833  
822 834 /*!
823 835 * \ingroup formats
824   - * \brief Plugin base class for reading matrices from disk.
  836 + * \brief Plugin base class for reading a template from disk.
825 837 *
826   - * A \em format is a br::File representing a matrix (ex. jpg image) on disk.
  838 + * A \em format is a br::File representing a template (ex. jpg image) on disk.
827 839 * br::File::suffix() is used to determine which plugin should handle the format.
828 840 */
829 841 class BR_EXPORT Format : public Object
... ... @@ -832,7 +844,7 @@ class BR_EXPORT Format : public Object
832 844  
833 845 public:
834 846 virtual ~Format() {}
835   - virtual QList<cv::Mat> read() const = 0; /*!< \brief Returns a list of matrices created by reading #br::Object::file. */
  847 + virtual Template read() const = 0; /*!< \brief Returns a br::Template created by reading #br::Object::file. */
836 848 };
837 849  
838 850 /*!
... ... @@ -1034,7 +1046,7 @@ public:
1034 1046 static QSharedPointer<Distance> fromAlgorithm(const QString &algorithm); /*!< \brief Retrieve an algorithm's distance. */
1035 1047 virtual void train(const TemplateList &src); /*!< \brief Train the distance. */
1036 1048 virtual void compare(const TemplateList &target, const TemplateList &query, Output *output) const; /*!< \brief Compare two template lists. */
1037   - inline float compare(const Template &target, const Template &query) const { return a * (_compare(target, query) - b); } /*!< \brief Compute the normalized distance between two templates. */
  1049 + float compare(const Template &target, const Template &query) const; /*!< \brief Compute the normalized distance between two templates. */
1038 1050  
1039 1051 private:
1040 1052 virtual void compareBlock(const TemplateList &target, const TemplateList &query, Output *output, int targetOffset, int queryOffset) const;
... ...
sdk/plugins/compare.cpp
... ... @@ -42,7 +42,7 @@ public:
42 42 INF,
43 43 L1,
44 44 L2,
45   - CosineSimilarity };
  45 + Cosine };
46 46  
47 47 private:
48 48 BR_PROPERTY(Metric, metric, L2)
... ... @@ -76,8 +76,8 @@ private:
76 76 case L2:
77 77 result = norm(a, b, NORM_L2);
78 78 break;
79   - case CosineSimilarity:
80   - result = cosineSimilarity(a, b);
  79 + case Cosine:
  80 + result = cosine(a, b);
81 81 break;
82 82 default:
83 83 qFatal("Invalid metric");
... ... @@ -89,33 +89,29 @@ private:
89 89 return -log(result+1);
90 90 }
91 91  
92   - static float cosineSimilarity(const Mat &a, const Mat &b)
  92 + static float cosine(const Mat &a, const Mat &b)
93 93 {
94   - assert((a.type() == CV_32FC1) && (b.type() == CV_32FC1));
95   - assert((a.rows == b.rows) && (a.cols == b.cols));
96   -
97   - float denom = 0;
98   - float tnum = 0;
99   - float qnum = 0;
  94 + float dot = 0;
  95 + float magA = 0;
  96 + float magB = 0;
100 97  
101 98 for (int row=0; row<a.rows; row++) {
102 99 for (int col=0; col<a.cols; col++) {
103   - float target = a.at<float>(row,col);
104   - float query = b.at<float>(row,col);
  100 + const float target = a.at<float>(row,col);
  101 + const float query = b.at<float>(row,col);
105 102  
106   - denom += target * query;
107   - tnum += target * target;
108   - qnum += query * query;
  103 + dot += target * query;
  104 + magA += target * target;
  105 + magB += query * query;
109 106 }
110 107 }
111 108  
112   - return denom / (sqrt(tnum)*sqrt(qnum));
  109 + return dot / (sqrt(magA)*sqrt(magB));
113 110 }
114 111 };
115 112  
116 113 BR_REGISTER(Distance, Dist)
117 114  
118   -
119 115 /*!
120 116 * \ingroup distances
121 117 * \brief Fast 8-bit L1 distance
... ...
sdk/plugins/eigen3.cpp
... ... @@ -276,10 +276,10 @@ class LDA : public Transform
276 276  
277 277 int dimsIn = ldaTrainingSet.first().m().rows * ldaTrainingSet.first().m().cols;
278 278  
279   - // MM ensures that classes values range from 0 to numClasses-1.
280   - QList<float> classes = trainingSet.labels<float>(); // PCA doesn't project metadata
  279 + // OpenBR ensures that class values range from 0 to numClasses-1.
  280 + QList<int> classes = trainingSet.labels<int>();
281 281 QMap<int, int> classCounts = trainingSet.labelCounts();
282   - int numClasses = classCounts.size();
  282 + const int numClasses = classCounts.size();
283 283  
284 284 // Map Eigen into OpenCV
285 285 Eigen::MatrixXd data = Eigen::MatrixXd(dimsIn, instances);
... ...
sdk/plugins/filter.cpp
... ... @@ -153,6 +153,7 @@ class CSDN : public UntrainableTransform
153 153 }
154 154 }
155 155  
  156 + m.convertTo(m, CV_8UC1);
156 157 dst = m;
157 158  
158 159 }
... ...
sdk/plugins/format.cpp
... ... @@ -14,9 +14,11 @@
14 14 * limitations under the License. *
15 15 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16 16  
  17 +#include <QDate>
17 18 #ifndef BR_EMBEDDED
18 19 #include <QNetworkAccessManager>
19 20 #include <QNetworkReply>
  21 +#include <QtXml>
20 22 #endif // BR_EMBEDDED
21 23 #include <opencv2/highgui/highgui.hpp>
22 24 #include <openbr_plugin.h>
... ... @@ -33,7 +35,7 @@ class csvFormat : public Format
33 35 {
34 36 Q_OBJECT
35 37  
36   - QList<Mat> read() const
  38 + Template read() const
37 39 {
38 40 QFile f(file.name);
39 41 f.open(QFile::ReadOnly);
... ... @@ -59,9 +61,7 @@ class csvFormat : public Format
59 61 }
60 62 }
61 63  
62   - QList<Mat> mats;
63   - mats.append(m);
64   - return mats;
  64 + return Template(m);
65 65 }
66 66 };
67 67  
... ... @@ -76,9 +76,9 @@ class DefaultFormat : public Format
76 76 {
77 77 Q_OBJECT
78 78  
79   - QList<Mat> read() const
  79 + Template read() const
80 80 {
81   - QList<Mat> mats;
  81 + Template t;
82 82  
83 83 if (file.name.startsWith("http://") || file.name.startsWith("www.")) {
84 84 #ifndef BR_EMBEDDED
... ... @@ -94,16 +94,16 @@ class DefaultFormat : public Format
94 94 delete reply;
95 95  
96 96 Mat m = imdecode(Mat(1, data.size(), CV_8UC1, data.data()), 1);
97   - if (m.data) mats.append(m);
  97 + if (m.data) t.append(m);
98 98 #endif // BR_EMBEDDED
99 99 } else {
100 100 QString prefix = "";
101 101 if (!QFileInfo(file.name).exists()) prefix = file.getString("path") + "/";
102 102 Mat m = imread((prefix+file.name).toStdString());
103   - if (m.data) mats.append(m);
  103 + if (m.data) t.append(m);
104 104 }
105 105  
106   - return mats;
  106 + return t;
107 107 }
108 108 };
109 109  
... ... @@ -118,7 +118,7 @@ class webcamFormat : public Format
118 118 {
119 119 Q_OBJECT
120 120  
121   - QList<Mat> read() const
  121 + Template read() const
122 122 {
123 123 static QScopedPointer<VideoCapture> videoCapture;
124 124  
... ... @@ -127,11 +127,73 @@ class webcamFormat : public Format
127 127  
128 128 Mat m;
129 129 videoCapture->read(m);
130   -
131   - return QList<Mat>() << m;
  130 + return Template(m);
132 131 }
133 132 };
134 133  
135 134 BR_REGISTER(Format, webcamFormat)
136 135  
  136 +#ifndef BR_EMBEDDED
  137 +/*!
  138 + * \ingroup formats
  139 + * \brief Decodes images from Base64 xml
  140 + * \author Scott Klum \cite sklum
  141 + * \author Josh Klontz \cite jklontz
  142 + */
  143 +class xmlFormat : public Format
  144 +{
  145 + Q_OBJECT
  146 +
  147 + Template read() const
  148 + {
  149 + QDomDocument doc(file);
  150 + QFile f(file);
  151 + if (!f.open(QIODevice::ReadOnly)) qFatal("xmlFormat::read unable to open %s for reading.", qPrintable(file.flat()));
  152 + if (!doc.setContent(&f)) qFatal("xmlFormat::read unable to parse %s.", qPrintable(file.flat()));
  153 + f.close();
  154 +
  155 + Template t;
  156 + QDomElement docElem = doc.documentElement();
  157 + QDomNode subject = docElem.firstChild();
  158 + while (!subject.isNull()) {
  159 + QDomNode fileNode = subject.firstChild();
  160 +
  161 + while (!fileNode.isNull()) {
  162 + QDomElement e = fileNode.toElement();
  163 +
  164 + if (e.tagName() == "FORMAL_IMG") {
  165 + QByteArray byteArray = QByteArray::fromBase64(qPrintable(e.text()));
  166 + Mat m = imdecode(Mat(1, byteArray.size(), CV_8UC1, byteArray.data()), CV_LOAD_IMAGE_ANYDEPTH);
  167 + if (!m.data) qWarning("xmlFormat::read failed to decode image data.");
  168 + t.append(m);
  169 + } else if ((e.tagName() == "RELEASE_IMG") ||
  170 + (e.tagName() == "PREBOOK_IMG") ||
  171 + (e.tagName() == "LPROFILE") ||
  172 + (e.tagName() == "RPROFILE")) {
  173 + // Ignore these other image fields for now
  174 + } else {
  175 + t.file.insert(e.tagName(), e.text());
  176 + }
  177 +
  178 + fileNode = fileNode.nextSibling();
  179 + }
  180 + subject = subject.nextSibling();
  181 + }
  182 +
  183 + // Calculate age
  184 + if (t.file.contains("DOB")) {
  185 + const QDate dob = QDate::fromString(t.file.getString("DOB").left(10), "yyyy-MM-dd");
  186 + const QDate current = QDate::currentDate();
  187 + int age = current.year() - dob.year();
  188 + if (current.month() < dob.month()) age--;
  189 + t.file.insert("Age", age);
  190 + }
  191 +
  192 + return t;
  193 + }
  194 +};
  195 +
  196 +BR_REGISTER(Format, xmlFormat)
  197 +#endif // BR_EMBEDDED
  198 +
137 199 #include "format.moc"
... ...
sdk/plugins/llvm.cpp
... ... @@ -65,6 +65,22 @@ static Matrix MatrixFromMat(const cv::Mat &amp;mat)
65 65 return m;
66 66 }
67 67  
  68 +static Mat MatFromMatrix(const Matrix &m)
  69 +{
  70 + int depth = -1;
  71 + switch (m.type()) {
  72 + case Matrix::u8: depth = CV_8U; break;
  73 + case Matrix::s8: depth = CV_8S; break;
  74 + case Matrix::u16: depth = CV_16U; break;
  75 + case Matrix::s16: depth = CV_16S; break;
  76 + case Matrix::s32: depth = CV_32S; break;
  77 + case Matrix::f32: depth = CV_32F; break;
  78 + case Matrix::f64: depth = CV_64F; break;
  79 + default: qFatal("Unrecognized matrix depth.");
  80 + }
  81 + return Mat(m.rows, m.columns, CV_MAKETYPE(depth, m.channels), m.data).clone();
  82 +}
  83 +
68 84 static void AllocateMatrixFromMat(Matrix &m, cv::Mat &mat)
69 85 {
70 86 int cvType = -1;
... ... @@ -230,26 +246,29 @@ struct MatrixBuilder : public Matrix
230 246 Value *compareLT(Value *i, Value *j) const { return isFloating() ? b->CreateFCmpOLT(i, j) : (isSigned() ? b->CreateICmpSLT(i, j) : b->CreateICmpULT(i, j)); }
231 247 Value *compareGT(Value *i, Value *j) const { return isFloating() ? b->CreateFCmpOGT(i, j) : (isSigned() ? b->CreateICmpSGT(i, j) : b->CreateICmpUGT(i, j)); }
232 248  
233   - static PHINode *beginLoop(IRBuilder<> &builder, Function *function, BasicBlock *parent, BasicBlock **current, const Twine &name = "") {
234   - *current = BasicBlock::Create(getGlobalContext(), "loop_"+name, function);
235   - builder.CreateBr(*current);
236   - builder.SetInsertPoint(*current);
237   - PHINode *j = builder.CreatePHI(Type::getInt32Ty(getGlobalContext()), 2, name);
238   - j->addIncoming(MatrixBuilder::zero(), parent);
239   - return j;
240   - }
241   - PHINode *beginLoop(BasicBlock *parent, BasicBlock **current, const Twine &name = "") const { return beginLoop(*b, f, parent, current, name); }
242   - static void endLoop(IRBuilder<> &builder, Function *function, BasicBlock *current, PHINode *j, Value *end, const Twine &name = "") {
243   - BasicBlock *loop = BasicBlock::Create(getGlobalContext(), "loop_"+name+"_end", function);
  249 + static PHINode *beginLoop(IRBuilder<> &builder, Function *function, BasicBlock *entry, BasicBlock *&loop, BasicBlock *&exit, Value *stop, const Twine &name = "") {
  250 + loop = BasicBlock::Create(getGlobalContext(), "loop_"+name, function);
244 251 builder.CreateBr(loop);
245 252 builder.SetInsertPoint(loop);
246   - Value *increment = builder.CreateAdd(j, MatrixBuilder::one(), "increment_"+name);
247   - j->addIncoming(increment, loop);
248   - BasicBlock *exit = BasicBlock::Create(getGlobalContext(), "loop_"+name+"_exit", function);
249   - builder.CreateCondBr(builder.CreateICmpNE(increment, end, "loop_"+name+"_test"), current, exit);
  253 +
  254 + PHINode *i = builder.CreatePHI(Type::getInt32Ty(getGlobalContext()), 2, name);
  255 + i->addIncoming(MatrixBuilder::zero(), entry);
  256 + Value *increment = builder.CreateAdd(i, MatrixBuilder::one(), "increment_"+name);
  257 + BasicBlock *body = BasicBlock::Create(getGlobalContext(), "loop_"+name+"_body", function);
  258 + i->addIncoming(increment, body);
  259 +
  260 + exit = BasicBlock::Create(getGlobalContext(), "loop_"+name+"_exit", function);
  261 + builder.CreateCondBr(builder.CreateICmpEQ(i, stop, "loop_"+name+"_test"), exit, body);
  262 + builder.SetInsertPoint(body);
  263 + return i;
  264 + }
  265 + PHINode *beginLoop(BasicBlock *entry, BasicBlock *&loop, BasicBlock *&exit, Value *stop, const Twine &name = "") const { return beginLoop(*b, f, entry, loop, exit, stop, name); }
  266 +
  267 + static void endLoop(IRBuilder<> &builder, BasicBlock *loop, BasicBlock *exit) {
  268 + builder.CreateBr(loop);
250 269 builder.SetInsertPoint(exit);
251 270 }
252   - void endLoop(BasicBlock *current, PHINode *j, Value *end, const Twine &name = "") const { endLoop(*b, f, current, j, end, name); }
  271 + void endLoop(BasicBlock *loop, BasicBlock *exit) const { endLoop(*b, loop, exit); }
253 272  
254 273 template <typename T>
255 274 inline static std::vector<T> toVector(T value) { std::vector<T> vector; vector.push_back(value); return vector; }
... ... @@ -440,14 +459,14 @@ private:
440 459 BasicBlock *entry = BasicBlock::Create(getGlobalContext(), "entry", function);
441 460 IRBuilder<> builder(entry);
442 461  
443   - BasicBlock *kernel;
444   - PHINode *i = MatrixBuilder::beginLoop(builder, function, entry, &kernel, "i");
  462 + BasicBlock *loop, *exit;
  463 + PHINode *i = MatrixBuilder::beginLoop(builder, function, entry, loop, exit, len, "i");
445 464  
446 465 Matrix n;
447 466 preallocate(m, n);
448 467 build(MatrixBuilder(m, src, &builder, function, "src"), MatrixBuilder(n, dst, &builder, function, "dst"), i);
449 468  
450   - MatrixBuilder::endLoop(builder, function, kernel, i, len, "i");
  469 + MatrixBuilder::endLoop(builder, loop, exit);
451 470  
452 471 builder.CreateRetVoid();
453 472 return function;
... ... @@ -533,14 +552,14 @@ private:
533 552 BasicBlock *entry = BasicBlock::Create(getGlobalContext(), "entry", function);
534 553 IRBuilder<> builder(entry);
535 554  
536   - BasicBlock *kernel;
537   - PHINode *i = MatrixBuilder::beginLoop(builder, function, entry, &kernel, "i");
  555 + BasicBlock *loop, *exit;
  556 + PHINode *i = MatrixBuilder::beginLoop(builder, function, entry, loop, exit, len, "i");
538 557  
539 558 Matrix o;
540 559 preallocate(m, n, o);
541 560 build(MatrixBuilder(m, srcA, &builder, function, "srcA"), MatrixBuilder(n, srcB, &builder, function, "srcB"), MatrixBuilder(o, dst, &builder, function, "dst"), i);
542 561  
543   - MatrixBuilder::endLoop(builder, function, kernel, i, len, "i");
  562 + MatrixBuilder::endLoop(builder, loop, exit);
544 563  
545 564 builder.CreateRetVoid();
546 565 return function;
... ... @@ -744,53 +763,52 @@ class sumTransform : public UnaryKernel
744 763 dst.deindex(i, &c, &x, &y, &t);
745 764 AllocaInst *sum = dst.autoAlloca(0, "sum");
746 765  
747   - QList<PHINode*> loops;
748   - QList<BasicBlock*> blocks;
749   - blocks.push_back(i->getParent());
  766 + QList<BasicBlock*> loops, exits;
  767 + loops.push_back(i->getParent());
750 768 Value *src_c, *src_x, *src_y, *src_t;
751 769  
752 770 if (frames && !src.singleFrame()) {
753   - BasicBlock *block;
754   - loops.append(dst.beginLoop(blocks.last(), &block, "src_t"));
755   - blocks.append(block);
756   - src_t = loops.last();
  771 + BasicBlock *loop, *exit;
  772 + src_t = dst.beginLoop(loops.last(), loop, exit, src.getFrames(), "src_t");
  773 + loops.append(loop);
  774 + exits.append(exit);
757 775 } else {
758 776 src_t = t;
759 777 }
760 778  
761 779 if (rows && !src.singleRow()) {
762   - BasicBlock *block;
763   - loops.append(dst.beginLoop(blocks.last(), &block, "src_y"));
764   - blocks.append(block);
765   - src_y = loops.last();
  780 + BasicBlock *loop, *exit;
  781 + src_y = dst.beginLoop(loops.last(), loop, exit, src.getRows(), "src_y");
  782 + loops.append(loop);
  783 + exits.append(exit);
766 784 } else {
767 785 src_y = y;
768 786 }
769 787  
770 788 if (columns && !src.singleColumn()) {
771   - BasicBlock *block;
772   - loops.append(dst.beginLoop(blocks.last(), &block, "src_x"));
773   - blocks.append(block);
774   - src_x = loops.last();
  789 + BasicBlock *loop, *exit;
  790 + src_x = dst.beginLoop(loops.last(), loop, exit, src.getColumns(), "src_x");
  791 + loops.append(loop);
  792 + exits.append(exit);
775 793 } else {
776 794 src_x = x;
777 795 }
778 796  
779 797 if (channels && !src.singleChannel()) {
780   - BasicBlock *block;
781   - loops.append(dst.beginLoop(blocks.last(), &block, "src_c"));
782   - blocks.append(block);
783   - src_c = loops.last();
  798 + BasicBlock *loop, *exit;
  799 + src_c = dst.beginLoop(loops.last(), loop, exit, src.getChannels(), "src_c");
  800 + loops.append(loop);
  801 + exits.append(exit);
784 802 } else {
785 803 src_c = c;
786 804 }
787 805  
788 806 dst.b->CreateStore(dst.add(dst.b->CreateLoad(sum), src.cast(src.load(src.aliasIndex(dst, src_c, src_x, src_y, src_t)), dst), "accumulate"), sum);
789 807  
790   - if (channels && !src.singleChannel()) dst.endLoop(blocks.takeLast(), loops.takeLast(), src.getChannels(), "src_c");
791   - if (columns && !src.singleColumn()) dst.endLoop(blocks.takeLast(), loops.takeLast(), src.getColumns(), "src_x");
792   - if (rows && !src.singleRow()) dst.endLoop(blocks.takeLast(), loops.takeLast(), src.getRows(), "src_y");
793   - if (frames && !src.singleFrame()) dst.endLoop(blocks.takeLast(), loops.takeLast(), src.getFrames(), "src_t");
  808 + if (channels && !src.singleChannel()) dst.endLoop(loops.takeLast(), exits.takeLast());
  809 + if (columns && !src.singleColumn()) dst.endLoop(loops.takeLast(), exits.takeLast());
  810 + if (rows && !src.singleRow()) dst.endLoop(loops.takeLast(), exits.takeLast());
  811 + if (frames && !src.singleFrame()) dst.endLoop(loops.takeLast(), exits.takeLast());
794 812  
795 813 dst.store(i, dst.b->CreateLoad(sum));
796 814 }
... ...
sdk/plugins/meta.cpp
... ... @@ -329,12 +329,15 @@ class LoadStoreTransform : public MetaTransform
329 329 {
330 330 Q_OBJECT
331 331 Q_PROPERTY(QString description READ get_description WRITE set_description RESET reset_description STORED false)
332   - Q_PROPERTY(br::Transform *transform READ get_transform WRITE set_transform RESET reset_transform STORED false)
333 332 BR_PROPERTY(QString, description, "Identity")
334   - BR_PROPERTY(br::Transform*, transform, NULL)
335 333  
  334 + Transform *transform;
336 335 QString baseName;
337 336  
  337 +public:
  338 + LoadStoreTransform() : transform(NULL) {}
  339 +
  340 +private:
338 341 void init()
339 342 {
340 343 if (transform != NULL) return;
... ... @@ -354,7 +357,7 @@ class LoadStoreTransform : public MetaTransform
354 357 QDataStream stream(&byteArray, QFile::WriteOnly);
355 358 stream << description;
356 359 transform->store(stream);
357   - QtUtils::writeFile(getFileName(), byteArray);
  360 + QtUtils::writeFile(baseName, byteArray);
358 361 }
359 362  
360 363 void project(const Template &src, Template &dst) const
... ... @@ -369,6 +372,7 @@ class LoadStoreTransform : public MetaTransform
369 372  
370 373 QString getFileName() const
371 374 {
  375 + if (QFileInfo(baseName).exists()) return baseName;
372 376 const QString file = Globals->sdkPath + "/share/openbr/models/transforms/" + baseName;
373 377 return QFileInfo(file).exists() ? file : QString();
374 378 }
... ...
sdk/plugins/misc.cpp
... ... @@ -34,18 +34,15 @@ class OpenTransform : public UntrainableMetaTransform
34 34 void project(const Template &src, Template &dst) const
35 35 {
36 36 if (Globals->verbose) qDebug("Opening %s", qPrintable(src.file.flat()));
37   - bool fto = false;
  37 + dst.file = src.file;
38 38 foreach (const File &file, src.file.split()) {
39 39 QScopedPointer<Format> format(Factory<Format>::make(file));
40   - QList<Mat> mats = format->read();
41   - if (mats.isEmpty()) {
42   - qWarning("Can't open %s", qPrintable(file.flat()));
43   - fto = true;
44   - }
45   - dst += mats;
  40 + Template t = format->read();
  41 + if (t.isEmpty()) qWarning("Can't open %s", qPrintable(file.flat()));
  42 + dst.append(t);
  43 + dst.file.append(t.file.localMetadata());
46 44 }
47   - dst.file = src.file;
48   - dst.file.insert("FTO", fto);
  45 + dst.file.insert("FTO", dst.isEmpty());
49 46 }
50 47 };
51 48  
... ... @@ -93,6 +90,27 @@ BR_REGISTER(Transform, ShowTransform)
93 90  
94 91 /*!
95 92 * \ingroup transforms
  93 + * \brief Prints the template's file to stdout or stderr.
  94 + * \author Josh Klontz \cite jklontz
  95 + */
  96 +class PrintTransform : public UntrainableMetaTransform
  97 +{
  98 + Q_OBJECT
  99 + Q_PROPERTY(bool error READ get_error WRITE set_error RESET reset_error)
  100 + BR_PROPERTY(bool, error, false)
  101 +
  102 + void project(const Template &src, Template &dst) const
  103 + {
  104 + dst = src;
  105 + if (error) qDebug("%s\n", qPrintable(src.file.flat()));
  106 + else printf("%s\n", qPrintable(src.file.flat()));
  107 + }
  108 +};
  109 +
  110 +BR_REGISTER(Transform, PrintTransform)
  111 +
  112 +/*!
  113 + * \ingroup transforms
96 114 * \brief Sets the template's matrix data to the br::File::name.
97 115 * \author Josh Klontz \cite jklontz
98 116 */
... ...
sdk/plugins/regions.cpp
... ... @@ -79,23 +79,29 @@ BR_REGISTER(Transform, ByRow)
79 79 class Cat : public UntrainableMetaTransform
80 80 {
81 81 Q_OBJECT
  82 + Q_PROPERTY(int partitions READ get_partitions WRITE set_partitions RESET reset_partitions)
  83 + BR_PROPERTY(int, partitions, 1)
82 84  
83 85 void project(const Template &src, Template &dst) const
84 86 {
85   - int vals = 0;
86   - foreach (const cv::Mat &m, src)
87   - vals += m.total() * m.channels();
88   -
89   - Mat cat(1, (int)vals, CV_32FC1);
90   - int offset = 0;
91   - foreach (const cv::Mat &m, src) {
92   - size_t size = m.total() * m.elemSize();
93   - memcpy(&cat.data[offset], m.ptr(), size);
94   - offset += size;
95   - }
96   -
97 87 dst.file = src.file;
98   - dst = cat;
  88 +
  89 + if (src.size() % partitions != 0)
  90 + qFatal("Cat %d partitions does not evenly divide %d matrices.", partitions, src.size());
  91 + QVector<int> sizes(partitions, 0);
  92 + for (int i=0; i<src.size(); i++)
  93 + sizes[i%partitions] += src[i].total() * src[i].channels();
  94 +
  95 + foreach (int size, sizes)
  96 + dst.append(Mat(1, size, CV_32FC1));
  97 +
  98 + QVector<int> offsets(partitions, 0);
  99 + for (int i=0; i<src.size(); i++) {
  100 + size_t size = src[i].total() * src[i].elemSize();
  101 + int j = i%partitions;
  102 + memcpy(&dst[j].data[offsets[j]], src[i].ptr(), size);
  103 + offsets[j] += size;
  104 + }
99 105 }
100 106 };
101 107  
... ...
1   -Subproject commit 1031e9e416427f5dd8e9f4e7ff4dd74632626c22
  1 +Subproject commit bcb22c79894d34b35891d4122e92e933380ec8ba
... ...
share/openbr/openbr.bib
1   -%% This BibTeX bibliography file was created using BibDesk.
2   -%% http://bibdesk.sourceforge.net/
3   -
4   -
5   -%% Created for Scott Klum at 2013-01-11 13:06:08 -0500
6   -
7   -
8   -%% Saved with string encoding Unicode (UTF-8)
9   -
10   -
  1 +@article{meyers08,
  2 + Author = {Meyers, E. and Wolf, L.},
  3 + Date-Added = {2013-01-11 18:46:12 +0000},
  4 + Date-Modified = {2013-01-11 18:47:57 +0000},
  5 + Journal = {Int. Journal of Computer Vision},
  6 + Number = {1},
  7 + Pages = {93-104},
  8 + Title = {Using biologically inspired features for face processing},
  9 + Volume = {76},
  10 + Year = {2008}}
11 11  
12 12 @misc{sklum,
13 13 Author = {Scott J. Klum},
... ... @@ -24,7 +24,7 @@
24 24 @misc{mburge,
25 25 Author = {Dr. Mark J. Burge},
26 26 Howpublished = {https://github.com/mburge},
27   - Title = {mburge at ieee.org}}
  27 + Title = {mburge at gmail.com}}
28 28  
29 29 @misc{bklare,
30 30 Author = {Dr. Brendan F. Klare},
... ...