Commit 3320ddac1be46c8fa1ee0372efca9d5f46034aa2

Authored by Scott Klum
2 parents e443d224 0e37f1a9

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

1   -Subproject commit 50452eccc85ed3092fd6b915f4dc52498e825c02
  1 +Subproject commit e8e79d30eb6bce6e295837179dbf4544c78739b5
... ...
openbr/core/classify.cpp
... ... @@ -14,8 +14,6 @@
14 14 * limitations under the License. *
15 15 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16 16  
17   -#include <QDebug>
18   -#include <QHash>
19 17 #include <openbr/openbr_plugin.h>
20 18  
21 19 #include "classify.h"
... ... @@ -24,7 +22,7 @@
24 22 // Helper struct for statistics accumulation
25 23 struct Counter
26 24 {
27   - int truePositive, falsePositive, falseNegative;
  25 + float truePositive, falsePositive, falseNegative;
28 26 Counter()
29 27 {
30 28 truePositive = 0;
... ... @@ -41,35 +39,44 @@ void br::EvalClassification(const QString &amp;predictedInput, const QString &amp;truthI
41 39 TemplateList truth(TemplateList::fromGallery(truthInput));
42 40 if (predicted.size() != truth.size()) qFatal("Input size mismatch.");
43 41  
44   - QHash<int, Counter> counters;
  42 + QHash<QString, Counter> counters;
45 43 for (int i=0; i<predicted.size(); i++) {
46 44 if (predicted[i].file.name != truth[i].file.name)
47 45 qFatal("Input order mismatch.");
48 46  
49   - const int trueLabel = truth[i].file.label();
50   - const int predictedLabel = predicted[i].file.label();
51   - if (trueLabel == predictedLabel) {
52   - counters[trueLabel].truePositive++;
53   - } else {
54   - counters[trueLabel].falseNegative++;
55   - counters[predictedLabel].falsePositive++;
  47 + // Typically these lists will be of length one, but this generalization allows measuring multi-class labeling accuracy.
  48 + QStringList predictedSubjects = predicted[i].file.get<QStringList>("Subject");
  49 + QStringList trueSubjects = truth[i].file.get<QStringList>("Subject");
  50 + foreach (const QString &subject, trueSubjects.toVector() /* Hack to copy the list. */) {
  51 + if (predictedSubjects.contains(subject)) {
  52 + counters[subject].truePositive++;
  53 + trueSubjects.removeOne(subject);
  54 + predictedSubjects.removeOne(subject);
  55 + } else {
  56 + counters[subject].falseNegative++;
  57 + }
56 58 }
  59 +
  60 + for (int i=0; i<trueSubjects.size(); i++)
  61 + foreach (const QString &subject, predictedSubjects)
  62 + counters[subject].falsePositive += 1.f / predictedSubjects.size();
57 63 }
58 64  
59 65 QSharedPointer<Output> output(Output::make("", FileList() << "Subject" << "Count" << "Precision" << "Recall" << "F-score", FileList(counters.size())));
60 66  
61 67 int tpc = 0;
62 68 int fnc = 0;
  69 + const QStringList keys = counters.keys();
63 70 for (int i=0; i<counters.size(); i++) {
64   - int trueLabel = counters.keys()[i];
65   - const Counter &counter = counters[trueLabel];
  71 + const QString &subject = keys[i];
  72 + const Counter &counter = counters[subject];
66 73 tpc += counter.truePositive;
67 74 fnc += counter.falseNegative;
68 75 const int count = counter.truePositive + counter.falseNegative;
69 76 const float precision = counter.truePositive / (float)(counter.truePositive + counter.falsePositive);
70 77 const float recall = counter.truePositive / (float)(counter.truePositive + counter.falseNegative);
71 78 const float fscore = 2 * precision * recall / (precision + recall);
72   - output->setRelative(trueLabel, i, 0);
  79 + output->setRelative(File("", subject).label(), i, 0);
73 80 output->setRelative(count, i, 1);
74 81 output->setRelative(precision, i, 2);
75 82 output->setRelative(recall, i, 3);
... ...
openbr/core/common.h
... ... @@ -102,7 +102,7 @@ void MinMax(const QList&lt;T&gt; &amp;vals, T *min, T *max)
102 102 template <typename T>
103 103 T Min(const QList<T> &vals)
104 104 {
105   - int min, max;
  105 + T min, max;
106 106 MinMax(vals, &min, &max);
107 107 return min;
108 108 }
... ... @@ -110,7 +110,7 @@ T Min(const QList&lt;T&gt; &amp;vals)
110 110 template <typename T>
111 111 T Max(const QList<T> &vals)
112 112 {
113   - int min, max;
  113 + T min, max;
114 114 MinMax(vals, &min, &max);
115 115 return max;
116 116 }
... ...
openbr/core/opencvutils.cpp
... ... @@ -157,6 +157,26 @@ Mat OpenCVUtils::toMatByRow(const QList&lt;Mat&gt; &amp;src)
157 157 return dst;
158 158 }
159 159  
  160 +QString OpenCVUtils::depthToString(const Mat &m)
  161 +{
  162 + switch (m.depth()) {
  163 + case CV_8U: return "8U";
  164 + case CV_8S: return "8S";
  165 + case CV_16U: return "16U";
  166 + case CV_16S: return "16S";
  167 + case CV_32S: return "32S";
  168 + case CV_32F: return "32F";
  169 + case CV_64F: return "64F";
  170 + default: qFatal("Unknown matrix depth!");
  171 + }
  172 + return "?";
  173 +}
  174 +
  175 +QString OpenCVUtils::typeToString(const cv::Mat &m)
  176 +{
  177 + return depthToString(m) + "C" + QString::number(m.channels());
  178 +}
  179 +
160 180 QString OpenCVUtils::elemToString(const Mat &m, int r, int c)
161 181 {
162 182 assert(m.channels() == 1);
... ...
openbr/core/opencvutils.h
... ... @@ -39,6 +39,8 @@ namespace OpenCVUtils
39 39 cv::Mat toMatByRow(const QList<cv::Mat> &src); // Data organized one row per row
40 40  
41 41 // From image
  42 + QString depthToString(const cv::Mat &m);
  43 + QString typeToString(const cv::Mat &m);
42 44 QString elemToString(const cv::Mat &m, int r, int c);
43 45 QString matrixToString(const cv::Mat &m);
44 46 QStringList matrixToStringList(const cv::Mat &m);
... ...
openbr/openbr_plugin.cpp
... ... @@ -923,24 +923,20 @@ void br::Context::messageHandler(QtMsgType type, const QMessageLogContext &amp;conte
923 923 {
924 924 // Something about this method is not thread safe, and will lead to crashes if qDebug
925 925 // statements are called from multiple threads. Unless we lock the whole thing...
926   - static QMutex generalLock(QMutex::Recursive);
  926 + static QMutex generalLock;
927 927 QMutexLocker locker(&generalLock);
928 928  
929 929 QString txt;
930   - switch (type) {
931   - case QtDebugMsg:
  930 + if (type == QtDebugMsg) {
932 931 if (Globals->quiet) return;
933 932 txt = QString("%1\n").arg(msg);
934   - break;
935   - case QtWarningMsg:
936   - txt = QString("Warning: %1\n").arg(msg);
937   - break;
938   - case QtCriticalMsg:
939   - txt = QString("Critical: %1\n").arg(msg);
940   - break;
941   - case QtFatalMsg:
942   - txt = QString("Fatal: %1\n").arg(msg);
943   - break;
  933 + } else {
  934 + switch (type) {
  935 + case QtWarningMsg: txt = QString("Warning: %1\n" ).arg(msg); break;
  936 + case QtCriticalMsg: txt = QString("Critical: %1\n").arg(msg); break;
  937 + default: txt = QString("Fatal: %1\n" ).arg(msg);
  938 + }
  939 + txt += " File: " + QString(context.file) + "\n Function: " + QString(context.function) + "\n Line: " + QString::number(context.line) + "\n";
944 940 }
945 941  
946 942 std::cerr << txt.toStdString();
... ... @@ -951,11 +947,8 @@ void br::Context::messageHandler(QtMsgType type, const QMessageLogContext &amp;conte
951 947 Globals->logFile.flush();
952 948 }
953 949  
954   - if (type == QtFatalMsg) {
955   - // Write debug output then close
956   - qDebug(" File: %s\n Function: %s\n Line: %d", qPrintable(context.file), qPrintable(context.function), context.line);
957   - Globals->finalize();
958   - }
  950 + if (type == QtFatalMsg)
  951 + abort(); // We abort so we can get a stack trace back to the code that triggered the message.
959 952 }
960 953  
961 954 Context *br::Globals = NULL;
... ... @@ -1027,7 +1020,7 @@ MatrixOutput *MatrixOutput::make(const FileList &amp;targetFiles, const FileList &amp;qu
1027 1020 /* MatrixOutput - protected methods */
1028 1021 QString MatrixOutput::toString(int row, int column) const
1029 1022 {
1030   - if (targetFiles[column] == "Label") {
  1023 + if (targetFiles[column] == "Subject") {
1031 1024 const int label = data.at<float>(row,column);
1032 1025 return Globals->subjects.key(label, QString::number(label));
1033 1026 }
... ...
openbr/plugins/algorithms.cpp
... ... @@ -50,6 +50,7 @@ class AlgorithmsInitializer : public Initializer
50 50 Globals->abbreviations.insert("SmallSIFT", "Open+LimitSize(512)+KeyPointDetector(SIFT)+KeyPointDescriptor(SIFT):KeyPointMatcher(BruteForce)");
51 51 Globals->abbreviations.insert("SmallSURF", "Open+LimitSize(512)+KeyPointDetector(SURF)+KeyPointDescriptor(SURF):KeyPointMatcher(BruteForce)");
52 52 Globals->abbreviations.insert("ColorHist", "Open+LimitSize(512)!EnsureChannels(3)+SplitChannels+Hist(256,0,8)+Cat+Normalize(L1):L2");
  53 + Globals->abbreviations.insert("ImageClassification", "Open+CropSquare+LimitSize(256)+Cvt(Gray)+Gradient+Bin(0,360,9,true)+Merge+Integral+RecursiveIntegralSampler(4,2,8,Singleton(KMeans(256)))+Cat+CvtFloat+Hist(256)+KNN(5,Dist(L1),false,5)+Rename(KNN,Subject)");
53 54  
54 55 // Hash
55 56 Globals->abbreviations.insert("FileName", "Name+Identity:Identical");
... ...
openbr/plugins/cluster.cpp
... ... @@ -17,6 +17,7 @@
17 17 #include <opencv2/flann/flann.hpp>
18 18  
19 19 #include "openbr_internal.h"
  20 +#include "openbr/core/common.h"
20 21 #include "openbr/core/opencvutils.h"
21 22  
22 23 using namespace cv;
... ... @@ -76,6 +77,68 @@ class KMeansTransform : public Transform
76 77  
77 78 BR_REGISTER(Transform, KMeansTransform)
78 79  
79   -}
  80 +/*!
  81 + * \ingroup transforms
  82 + * \brief K nearest neighbors classifier.
  83 + * \author Josh Klontz \cite jklontz
  84 + */
  85 +class KNNTransform : public Transform
  86 +{
  87 + Q_OBJECT
  88 + Q_PROPERTY(int k READ get_k WRITE set_k RESET reset_k STORED false)
  89 + Q_PROPERTY(br::Distance *distance READ get_distance WRITE set_distance RESET reset_distance STORED false)
  90 + Q_PROPERTY(bool weighted READ get_weighted WRITE set_weighted RESET reset_weighted STORED false)
  91 + Q_PROPERTY(int numSubjects READ get_numSubjects WRITE set_numSubjects RESET reset_numSubjects STORED false)
  92 + BR_PROPERTY(int, k, 1)
  93 + BR_PROPERTY(br::Distance*, distance, NULL)
  94 + BR_PROPERTY(bool, weighted, false)
  95 + BR_PROPERTY(int, numSubjects, 1)
  96 +
  97 + TemplateList gallery;
  98 +
  99 + void train(const TemplateList &data)
  100 + {
  101 + distance->train(data);
  102 + gallery = data;
  103 + }
  104 +
  105 + void project(const Template &src, Template &dst) const
  106 + {
  107 + QList< QPair<float, int> > sortedScores = Common::Sort(distance->compare(gallery, src), true);
  108 +
  109 + QStringList subjects;
  110 + for (int i=0; i<numSubjects; i++) {
  111 + QHash<QString, float> votes;
  112 + const int max = (k < 1) ? sortedScores.size() : std::min(k, sortedScores.size());
  113 + for (int j=0; j<max; j++)
  114 + votes[gallery[sortedScores[j].second].file.subject()] += (weighted ? sortedScores[j].first : 1);
  115 + subjects.append(votes.keys()[votes.values().indexOf(Common::Max(votes.values()))]);
  116 +
  117 + // Remove subject from consideration
  118 + if (subjects.size() < numSubjects)
  119 + for (int j=sortedScores.size()-1; j>=0; j--)
  120 + if (gallery[sortedScores[j].second].file.subject() == subjects.last())
  121 + sortedScores.removeAt(j);
  122 + }
  123 +
  124 + dst.file.set("KNN", subjects.size() > 1 ? "[" + subjects.join(",") + "]" : subjects.first());
  125 + }
  126 +
  127 + void store(QDataStream &stream) const
  128 + {
  129 + stream << gallery;
  130 + }
  131 +
  132 + void load(QDataStream &stream)
  133 + {
  134 + stream >> gallery;
  135 + }
  136 +};
  137 +
  138 +BR_REGISTER(Transform, KNNTransform)
  139 +
  140 +
  141 +
  142 +} // namespace br
80 143  
81 144 #include "cluster.moc"
... ...
openbr/plugins/crop.cpp
... ... @@ -151,6 +151,48 @@ class CropBlackTransform : public UntrainableTransform
151 151  
152 152 BR_REGISTER(Transform, CropBlackTransform)
153 153  
  154 +/*!
  155 + * \ingroup transforms
  156 + * \brief Divide the matrix into 4 smaller matricies of equal size.
  157 + * \author Josh Klontz \cite jklontz
  158 + */
  159 +class SubdivideTransform : public UntrainableTransform
  160 +{
  161 + Q_OBJECT
  162 +
  163 + void project(const Template &src, Template &dst) const
  164 + {
  165 + const Mat &m = src;
  166 + const int subrows = m.rows/2;
  167 + const int subcolumns = m.cols/2;
  168 + dst.append(Mat(m,Rect(0, 0, subcolumns, subrows)).clone());
  169 + dst.append(Mat(m,Rect(subcolumns, 0, subcolumns, subrows)).clone());
  170 + dst.append(Mat(m,Rect(0, subrows, subcolumns, subrows)).clone());
  171 + dst.append(Mat(m,Rect(subcolumns, subrows, subcolumns, subrows)).clone());
  172 + }
  173 +};
  174 +
  175 +BR_REGISTER(Transform, SubdivideTransform)
  176 +
  177 +/*!
  178 + * \ingroup transforms
  179 + * \brief Trim the image so the width and the height are the same size.
  180 + * \author Josh Klontz \cite jklontz
  181 + */
  182 +class CropSquareTransform : public UntrainableTransform
  183 +{
  184 + Q_OBJECT
  185 +
  186 + void project(const Template &src, Template &dst) const
  187 + {
  188 + const Mat &m = src;
  189 + const int newSize = min(m.rows, m.cols);
  190 + dst = Mat(m, Rect((m.cols-newSize)/2, (m.rows-newSize)/2, newSize, newSize));
  191 + }
  192 +};
  193 +
  194 +BR_REGISTER(Transform, CropSquareTransform)
  195 +
154 196 } // namespace br
155 197  
156 198 #include "crop.moc"
... ...
openbr/plugins/gallery.cpp
... ... @@ -212,10 +212,52 @@ class DefaultGallery : public Gallery
212 212 format->write(t);
213 213 }
214 214 };
  215 +
215 216 BR_REGISTER(Gallery, DefaultGallery)
216 217  
217 218 /*!
218 219 * \ingroup galleries
  220 + * \brief Combine all templates into one large matrix and process it as a br::Format
  221 + * \author Josh Klontz \cite jklontz
  222 + */
  223 +class matrixGallery : public Gallery
  224 +{
  225 + Q_OBJECT
  226 + Q_PROPERTY(const QString extension READ get_extension WRITE set_extension RESET reset_extension STORED false)
  227 + BR_PROPERTY(QString, extension, "mtx")
  228 +
  229 + TemplateList templates;
  230 +
  231 + ~matrixGallery()
  232 + {
  233 + if (templates.isEmpty())
  234 + return;
  235 +
  236 + QScopedPointer<Format> format(Factory<Format>::make(getFormat()));
  237 + format->write(Template(file, OpenCVUtils::toMat(templates.data())));
  238 + }
  239 +
  240 + File getFormat() const
  241 + {
  242 + return file.name.left(file.name.size() - file.suffix().size()) + extension;
  243 + }
  244 +
  245 + TemplateList readBlock(bool *done)
  246 + {
  247 + *done = true;
  248 + return TemplateList() << getFormat();
  249 + }
  250 +
  251 + void write(const Template &t)
  252 + {
  253 + templates.append(t);
  254 + }
  255 +};
  256 +
  257 +BR_REGISTER(Gallery, matrixGallery)
  258 +
  259 +/*!
  260 + * \ingroup galleries
219 261 * \brief Treat a video as a gallery, making a single template from each frame
220 262 * \author Charles Otto \cite caotto
221 263 */
... ... @@ -499,6 +541,8 @@ BR_REGISTER(Gallery, csvGallery)
499 541 class txtGallery : public Gallery
500 542 {
501 543 Q_OBJECT
  544 + Q_PROPERTY(QString metadataKey READ get_metadataKey WRITE set_metadataKey RESET reset_metadataKey STORED false)
  545 + BR_PROPERTY(QString, metadataKey, "")
502 546  
503 547 QStringList lines;
504 548  
... ... @@ -521,7 +565,7 @@ class txtGallery : public Gallery
521 565  
522 566 void write(const Template &t)
523 567 {
524   - lines.append(t.file.flat());
  568 + lines.append(metadataKey.isEmpty() ? t.file.flat() : t.file.get<QString>(metadataKey));
525 569 }
526 570 };
527 571  
... ... @@ -762,6 +806,41 @@ class googleGallery : public Gallery
762 806  
763 807 BR_REGISTER(Gallery, googleGallery)
764 808  
  809 +/*!
  810 + * \ingroup galleries
  811 + * \brief Count the number of templates.
  812 + * \author Josh Klontz \cite jklontz
  813 + */
  814 +class TemplateCountGallery : public Gallery
  815 +{
  816 + Q_OBJECT
  817 + int count;
  818 +
  819 + ~TemplateCountGallery()
  820 + {
  821 + printf("%d\n", count);
  822 + }
  823 +
  824 + void init()
  825 + {
  826 + count = 0;
  827 + }
  828 +
  829 + TemplateList readBlock(bool *done)
  830 + {
  831 + *done = true;
  832 + return TemplateList() << file;
  833 + }
  834 +
  835 + void write(const Template &t)
  836 + {
  837 + (void) t;
  838 + count++;
  839 + }
  840 +};
  841 +
  842 +BR_REGISTER(Gallery, TemplateCountGallery)
  843 +
765 844 } // namespace br
766 845  
767 846 #include "gallery.moc"
... ...
openbr/plugins/independent.cpp
... ... @@ -162,6 +162,71 @@ class IndependentTransform : public MetaTransform
162 162  
163 163 BR_REGISTER(Transform, IndependentTransform)
164 164  
  165 +/*!
  166 + * \ingroup transforms
  167 + * \brief A globally shared transform.
  168 + * \author Josh Klontz \cite jklontz
  169 + */
  170 +class SingletonTransform : public MetaTransform
  171 +{
  172 + Q_OBJECT
  173 + Q_PROPERTY(QString description READ get_description WRITE set_description RESET reset_description STORED false)
  174 + BR_PROPERTY(QString, description, "Identity")
  175 +
  176 + static QMutex mutex;
  177 + static QHash<QString,Transform*> transforms;
  178 + static QHash<QString,int> trainingReferenceCounts;
  179 + static QHash<QString,TemplateList> trainingData;
  180 +
  181 + Transform *transform;
  182 +
  183 + void init()
  184 + {
  185 + QMutexLocker locker(&mutex);
  186 + if (!transforms.contains(description)) {
  187 + transforms.insert(description, make(description));
  188 + trainingReferenceCounts.insert(description, 0);
  189 + }
  190 +
  191 + transform = transforms[description];
  192 + trainingReferenceCounts[description]++;
  193 + }
  194 +
  195 + void train(const TemplateList &data)
  196 + {
  197 + QMutexLocker locker(&mutex);
  198 + trainingData[description].append(data);
  199 + trainingReferenceCounts[description]--;
  200 + if (trainingReferenceCounts[description] > 0) return;
  201 + transform->train(trainingData[description]);
  202 + trainingData[description].clear();
  203 + }
  204 +
  205 + void project(const Template &src, Template &dst) const
  206 + {
  207 + transform->project(src, dst);
  208 + }
  209 +
  210 + void store(QDataStream &stream) const
  211 + {
  212 + if (transform->parent() == this)
  213 + transform->store(stream);
  214 + }
  215 +
  216 + void load(QDataStream &stream)
  217 + {
  218 + if (transform->parent() == this)
  219 + transform->load(stream);
  220 + }
  221 +};
  222 +
  223 +QMutex SingletonTransform::mutex;
  224 +QHash<QString,Transform*> SingletonTransform::transforms;
  225 +QHash<QString,int> SingletonTransform::trainingReferenceCounts;
  226 +QHash<QString,TemplateList> SingletonTransform::trainingData;
  227 +
  228 +BR_REGISTER(Transform, SingletonTransform)
  229 +
165 230 } // namespace br
166 231  
167 232 #include "independent.moc"
... ...
openbr/plugins/integral.cpp
... ... @@ -154,14 +154,14 @@ class RecursiveIntegralSamplerTransform : public Transform
154 154 }
155 155 }
156 156  
157   - static void integralHistogram(const Mat &src, const int x, const int y, const int rows, const int columns, Mat &dst, int index)
  157 + static void integralHistogram(const Mat &src, const int x, const int y, const int width, const int height, Mat &dst, int index)
158 158 {
159 159 const int channels = src.channels();
160 160 OutputDescriptor(dst.ptr<float>(index), channels, 1) =
161   - ( InputDescriptor(src.ptr<qint32>(y+rows, x+columns), channels, 1)
162   - - InputDescriptor(src.ptr<qint32>(y, x+columns), channels, 1)
163   - - InputDescriptor(src.ptr<qint32>(y+rows, x), channels, 1)
164   - + InputDescriptor(src.ptr<qint32>(y, x), channels, 1)).cast<float>()/(rows*columns);
  161 + ( InputDescriptor(src.ptr<qint32>(y+height, x+width), channels, 1)
  162 + - InputDescriptor(src.ptr<qint32>(y, x+width), channels, 1)
  163 + - InputDescriptor(src.ptr<qint32>(y+height, x), channels, 1)
  164 + + InputDescriptor(src.ptr<qint32>(y, x), channels, 1)).cast<float>()/(height*width);
165 165 }
166 166  
167 167 void computeDescriptor(const Mat &src, Mat &dst) const
... ... @@ -171,11 +171,11 @@ class RecursiveIntegralSamplerTransform : public Transform
171 171 const int columns = src.cols-1;
172 172  
173 173 Mat tmp(5, channels, CV_32FC1);
174   - integralHistogram(src, 0, 0, rows/2, columns/2, tmp, 0);
175   - integralHistogram(src, 0, columns/2, rows/2, columns/2, tmp, 1);
176   - integralHistogram(src, rows/2, 0, rows/2, columns/2, tmp, 2);
177   - integralHistogram(src, rows/2, columns/2, rows/2, columns/2, tmp, 3);
178   - integralHistogram(src, rows/4, columns/4, rows/2, columns/2, tmp, 4);
  174 + integralHistogram(src, 0, 0, columns/2, rows/2, tmp, 0);
  175 + integralHistogram(src, columns/2, 0, columns/2, rows/2, tmp, 1);
  176 + integralHistogram(src, 0, rows/2, columns/2, rows/2, tmp, 2);
  177 + integralHistogram(src, columns/2, rows/2, columns/2, rows/2, tmp, 3);
  178 + integralHistogram(src, columns/4, rows/4, columns/2, rows/2, tmp, 4);
179 179 const SecondOrderInputDescriptor a(tmp.ptr<float>(0), channels, 1);
180 180 const SecondOrderInputDescriptor b(tmp.ptr<float>(1), channels, 1);
181 181 const SecondOrderInputDescriptor c(tmp.ptr<float>(2), channels, 1);
... ... @@ -188,6 +188,7 @@ class RecursiveIntegralSamplerTransform : public Transform
188 188 OutputDescriptor(dst.ptr<float>(2), channels, 1) = ((a+b)-(c+d))/2.f;
189 189 OutputDescriptor(dst.ptr<float>(3), channels, 1) = ((a+c)-(b+d))/2.f;
190 190 OutputDescriptor(dst.ptr<float>(4), channels, 1) = ((a+d)-(b+c))/2.f;
  191 + dst = dst.reshape(1, 1);
191 192 }
192 193  
193 194 Template subdivide(const Template &src) const
... ...
openbr/plugins/misc.cpp
... ... @@ -60,20 +60,18 @@ class PrintTransform : public UntrainableMetaTransform
60 60 Q_OBJECT
61 61 Q_PROPERTY(bool error READ get_error WRITE set_error RESET reset_error)
62 62 Q_PROPERTY(bool data READ get_data WRITE set_data RESET reset_data)
63   - Q_PROPERTY(bool nTemplates READ get_data WRITE set_data RESET reset_data)
64 63 BR_PROPERTY(bool, error, true)
65 64 BR_PROPERTY(bool, data, false)
66   - BR_PROPERTY(bool, size, false)
67 65  
68 66 void project(const Template &src, Template &dst) const
69 67 {
70 68 dst = src;
71 69 const QString nameString = src.file.flat();
72 70 const QString dataString = data ? OpenCVUtils::matrixToString(src)+"\n" : QString();
73   - const QString nTemplates = size ? QString::number(src.size()) : QString();
74   - qDebug() << "Dimensionality: " << src.first().cols;
75   - if (error) qDebug("%s\n%s\n%s", qPrintable(nameString), qPrintable(dataString), qPrintable(nTemplates));
76   - else printf("%s\n%s\n%s", qPrintable(nameString), qPrintable(dataString), qPrintable(nTemplates));
  71 + QStringList matricies;
  72 + foreach (const Mat &m, src)
  73 + matricies.append(QString::number(m.rows) + "x" + QString::number(m.cols) + "_" + OpenCVUtils::typeToString(m));
  74 + fprintf(error ? stderr : stdout, "%s\n %s\n%s", qPrintable(nameString), qPrintable(matricies.join(",")), qPrintable(dataString));
77 75 }
78 76 };
79 77  
... ... @@ -402,13 +400,14 @@ BR_REGISTER(Transform, AsTransform)
402 400  
403 401 /*!
404 402 * \ingroup transforms
405   - * \brief Change the template label using a regular expresion matched to the file's base name.
  403 + * \brief Change the template subject using a regular expresion matched to the file's base name.
  404 + * \author Josh Klontz \cite jklontz
406 405 */
407   -class RelabelTransform : public UntrainableMetaTransform
  406 +class SubjectTransform : public UntrainableMetaTransform
408 407 {
409 408 Q_OBJECT
410 409 Q_PROPERTY(QString regexp READ get_regexp WRITE set_regexp RESET reset_regexp STORED false)
411   - BR_PROPERTY(QString, regexp, "")
  410 + BR_PROPERTY(QString, regexp, "(.*)")
412 411  
413 412 void project(const Template &src, Template &dst) const
414 413 {
... ... @@ -417,11 +416,11 @@ class RelabelTransform : public UntrainableMetaTransform
417 416 QRegularExpressionMatch match = re.match(dst.file.baseName());
418 417 if (!match.hasMatch())
419 418 qFatal("Unable to match regular expression \"%s\" to base name \"%s\"!", qPrintable(regexp), qPrintable(dst.file.baseName()));
420   - dst.file.set("Label", match.captured(match.lastCapturedIndex()));
  419 + dst.file.set("Subject", match.captured(match.lastCapturedIndex()));
421 420 }
422 421 };
423 422  
424   -BR_REGISTER(Transform, RelabelTransform)
  423 +BR_REGISTER(Transform, SubjectTransform)
425 424  
426 425 /*!
427 426 * \ingroup transforms
... ...
openbr/plugins/regions.cpp
... ... @@ -92,15 +92,15 @@ class CatTransform : public UntrainableMetaTransform
92 92 qFatal("%d partitions does not evenly divide %d matrices.", partitions, src.size());
93 93 QVector<int> sizes(partitions, 0);
94 94 for (int i=0; i<src.size(); i++)
95   - sizes[i%partitions] += src[i].total() * src[i].channels();
  95 + sizes[i%partitions] += src[i].total();
96 96  
97 97 foreach (int size, sizes)
98   - dst.append(Mat(1, size, CV_32FC1));
  98 + dst.append(Mat(1, size, src.m().type()));
99 99  
100 100 QVector<int> offsets(partitions, 0);
101 101 for (int i=0; i<src.size(); i++) {
102 102 size_t size = src[i].total() * src[i].elemSize();
103   - int j = i%partitions;
  103 + int j = i % partitions;
104 104 memcpy(&dst[j].data[offsets[j]], src[i].ptr(), size);
105 105 offsets[j] += size;
106 106 }
... ... @@ -111,6 +111,25 @@ BR_REGISTER(Transform, CatTransform)
111 111  
112 112 /*!
113 113 * \ingroup transforms
  114 + * \brief Reshape the each matrix to the specified number of rows.
  115 + * \author Josh Klontz \cite jklontz
  116 + */
  117 +class ReshapeTransform : public UntrainableTransform
  118 +{
  119 + Q_OBJECT
  120 + Q_PROPERTY(int rows READ get_rows WRITE set_rows RESET reset_rows STORED false)
  121 + BR_PROPERTY(int, rows, 1)
  122 +
  123 + void project(const Template &src, Template &dst) const
  124 + {
  125 + dst = src.m().reshape(src.m().channels(), rows);
  126 + }
  127 +};
  128 +
  129 +BR_REGISTER(Transform, ReshapeTransform)
  130 +
  131 +/*!
  132 + * \ingroup transforms
114 133 * \brief Wraps OpenCV merge
115 134 * \author Josh Klontz \cite jklontz
116 135 */
... ...
openbr/plugins/svm.cpp
... ... @@ -17,18 +17,98 @@
17 17 #include <QTemporaryFile>
18 18 #include <opencv2/core/core.hpp>
19 19 #include <opencv2/ml/ml.hpp>
20   -#include "openbr_internal.h"
21 20  
  21 +#include "openbr_internal.h"
22 22 #include "openbr/core/opencvutils.h"
23 23  
  24 +using namespace cv;
  25 +
24 26 namespace br
25 27 {
26 28  
  29 +static void storeSVM(float a, float b, const SVM &svm, QDataStream &stream)
  30 +{
  31 + stream << a << b;
  32 +
  33 + // Create local file
  34 + QTemporaryFile tempFile;
  35 + tempFile.open();
  36 + tempFile.close();
  37 +
  38 + // Save SVM to local file
  39 + svm.save(qPrintable(tempFile.fileName()));
  40 +
  41 + // Copy local file contents to stream
  42 + tempFile.open();
  43 + QByteArray data = tempFile.readAll();
  44 + tempFile.close();
  45 + stream << data;
  46 +}
  47 +
  48 +static void loadSVM(float &a, float &b, SVM &svm, QDataStream &stream)
  49 +{
  50 + stream >> a >> b;
  51 +
  52 + // Copy local file contents from stream
  53 + QByteArray data;
  54 + stream >> data;
  55 +
  56 + // Create local file
  57 + QTemporaryFile tempFile(QDir::tempPath()+"/SVM");
  58 + tempFile.open();
  59 + tempFile.write(data);
  60 + tempFile.close();
  61 +
  62 + // Load SVM from local file
  63 + svm.load(qPrintable(tempFile.fileName()));
  64 +}
  65 +
  66 +static void trainSVM(float &a, float &b, SVM &svm, Mat data, Mat lab, int kernel, int type, float C, float gamma)
  67 +{
  68 + if ((type == CvSVM::EPS_SVR) || (type == CvSVM::NU_SVR)) {
  69 + // Scale labels to [-1,1]
  70 + double min, max;
  71 + minMaxLoc(lab, &min, &max);
  72 + if (max > min) {
  73 + a = 2.0/(max-min);
  74 + b = -(min*a+1);
  75 + lab = (lab * a) + b;
  76 + }
  77 + } else {
  78 + a = 1;
  79 + b = 0;
  80 + }
  81 +
  82 + if (data.type() != CV_32FC1)
  83 + qFatal("Expected single channel floating point training data.");
  84 +
  85 + CvSVMParams params;
  86 + params.kernel_type = kernel;
  87 + params.svm_type = type;
  88 + params.p = 0.1;
  89 + params.nu = 0.5;
  90 + if ((C == -1) || ((gamma == -1) && (kernel == CvSVM::RBF))) {
  91 + try {
  92 + svm.train_auto(data, lab, Mat(), Mat(), params, 5);
  93 + } catch (...) {
  94 + qWarning("Some classes do not contain sufficient examples or are not discriminative enough for accurate SVM classification.");
  95 + svm.train(data, lab);
  96 + }
  97 + } else {
  98 + params.C = C;
  99 + params.gamma = gamma;
  100 + svm.train(data, lab, Mat(), Mat(), params);
  101 + }
  102 +
  103 + CvSVMParams p = svm.get_params();
  104 + qDebug("SVM C = %f Gamma = %f Support Vectors = %d", p.C, p.gamma, svm.get_support_vector_count());
  105 +}
  106 +
27 107 /*!
28 108 * \ingroup transforms
29 109 * \brief C. Burges. "A tutorial on support vector machines for pattern recognition,"
30   - * Knowledge Discovery and Data Mining 2(2), 1998.
31 110 * \author Josh Klontz \cite jklontz
  111 + * Knowledge Discovery and Data Mining 2(2), 1998.
32 112 */
33 113 class SVMTransform : public Transform
34 114 {
... ... @@ -41,16 +121,11 @@ class SVMTransform : public Transform
41 121 Q_PROPERTY(float gamma READ get_gamma WRITE set_gamma RESET reset_gamma STORED false)
42 122  
43 123 public:
44   - /*!
45   - * \brief The Kernel enum
46   - */
47 124 enum Kernel { Linear = CvSVM::LINEAR,
48 125 Poly = CvSVM::POLY,
49 126 RBF = CvSVM::RBF,
50 127 Sigmoid = CvSVM::SIGMOID };
51   - /*!
52   - * \brief The Type enum
53   - */
  128 +
54 129 enum Type { C_SVC = CvSVM::C_SVC,
55 130 NU_SVC = CvSVM::NU_SVC,
56 131 ONE_CLASS = CvSVM::ONE_CLASS,
... ... @@ -63,99 +138,110 @@ private:
63 138 BR_PROPERTY(float, C, -1)
64 139 BR_PROPERTY(float, gamma, -1)
65 140  
66   - cv::SVM svm;
  141 + SVM svm;
67 142 float a, b;
68 143  
69   -public:
70   - SVMTransform() : a(1), b(0) {}
71   -
72   -private:
73 144 void train(const TemplateList &_data)
74 145 {
75   - cv::Mat data = OpenCVUtils::toMat(_data.data());
76   - cv::Mat lab = OpenCVUtils::toMat(_data.labels<float>());
77   -
78   - if ((type == EPS_SVR) || (type == NU_SVR)) {
79   - // Scale labels to [-1,1]
80   - double min, max;
81   - cv::minMaxLoc(lab, &min, &max);
82   - if (max > min) {
83   - a = 2.0/(max-min);
84   - b = -(min*a+1);
85   - lab = (lab * a) + b;
86   - }
87   - }
88   -
89   - if (data.type() != CV_32FC1)
90   - qFatal("Expected single channel floating point training data.");
91   -
92   - CvSVMParams params;
93   - params.kernel_type = kernel;
94   - params.svm_type = type;
95   - params.p = 0.1;
96   - params.nu = 0.5;
97   - if ((C == -1) || ((gamma == -1) && (int(kernel) != int(CvSVM::LINEAR)))) {
98   - try {
99   - svm.train_auto(data, lab, cv::Mat(), cv::Mat(), params, 5);
100   - } catch (...) {
101   - qWarning("Some classes do not contain sufficient examples or are not discriminative enough for accurate SVM classification.");
102   - svm.train(data, lab);
103   - }
104   - } else {
105   - params.C = C;
106   - params.gamma = gamma;
107   - svm.train(data, lab, cv::Mat(), cv::Mat(), params);
108   - }
109   -
110   - CvSVMParams p = svm.get_params();
111   - qDebug("SVM C = %f Gamma = %f Support Vectors = %d", p.C, p.gamma, svm.get_support_vector_count());
  146 + Mat data = OpenCVUtils::toMat(_data.data());
  147 + Mat lab = OpenCVUtils::toMat(_data.labels<float>());
  148 + trainSVM(a, b, svm, data, lab, kernel, type, C, gamma);
112 149 }
113 150  
114 151 void project(const Template &src, Template &dst) const
115 152 {
116 153 dst = src;
117   - dst.file.set("Label", ((svm.predict(src.m().reshape(0, 1)) - b)/a));
  154 + dst.file.set("Label", ((svm.predict(src.m().reshape(1, 1)) - b)/a));
118 155 }
119 156  
120 157 void store(QDataStream &stream) const
121 158 {
122   - stream << a << b;
  159 + storeSVM(a, b, svm, stream);
  160 + }
123 161  
124   - // Create local file
125   - QTemporaryFile tempFile;
126   - tempFile.open();
127   - tempFile.close();
  162 + void load(QDataStream &stream)
  163 + {
  164 + loadSVM(a, b, svm, stream);
  165 + }
  166 +};
128 167  
129   - // Save SVM to local file
130   - svm.save(qPrintable(tempFile.fileName()));
  168 +BR_REGISTER(Transform, SVMTransform)
131 169  
132   - // Copy local file contents to stream
133   - tempFile.open();
134   - QByteArray data = tempFile.readAll();
135   - tempFile.close();
136   - stream << data;
137   - }
  170 +/*!
  171 + * \ingroup Distances
  172 + * \brief SVM Regression on template absolute differences.
  173 + * \author Josh Klontz
  174 + */
  175 +class SVMDistance : public Distance
  176 +{
  177 + Q_OBJECT
  178 + Q_ENUMS(Kernel)
  179 + Q_ENUMS(Type)
  180 + Q_PROPERTY(Kernel kernel READ get_kernel WRITE set_kernel RESET reset_kernel STORED false)
  181 + Q_PROPERTY(Type type READ get_type WRITE set_type RESET reset_type STORED false)
138 182  
139   - void load(QDataStream &stream)
  183 +public:
  184 + enum Kernel { Linear = CvSVM::LINEAR,
  185 + Poly = CvSVM::POLY,
  186 + RBF = CvSVM::RBF,
  187 + Sigmoid = CvSVM::SIGMOID };
  188 +
  189 + enum Type { C_SVC = CvSVM::C_SVC,
  190 + NU_SVC = CvSVM::NU_SVC,
  191 + ONE_CLASS = CvSVM::ONE_CLASS,
  192 + EPS_SVR = CvSVM::EPS_SVR,
  193 + NU_SVR = CvSVM::NU_SVR};
  194 +
  195 +private:
  196 + BR_PROPERTY(Kernel, kernel, Linear)
  197 + BR_PROPERTY(Type, type, EPS_SVR)
  198 +
  199 + SVM svm;
  200 + float a, b;
  201 +
  202 + void train(const TemplateList &src)
140 203 {
141   - stream >> a >> b;
  204 + const Mat data = OpenCVUtils::toMat(src.data());
  205 + const QList<int> lab = src.labels<int>();
  206 +
  207 + const int instances = data.rows * (data.rows+1) / 2;
  208 + Mat deltaData(instances, data.cols, data.type());
  209 + Mat deltaLab(instances, 1, CV_32FC1);
  210 + int index = 0;
  211 + for (int i=0; i<data.rows; i++)
  212 + for (int j=i; j<data.rows; j++) {
  213 + const bool match = lab[i] == lab[j];
  214 + if (!match && (type == ONE_CLASS))
  215 + continue;
  216 + absdiff(data.row(i), data.row(j), deltaData.row(index));
  217 + deltaLab.at<float>(index, 0) = (match ? 1 : 0);
  218 + index++;
  219 + }
  220 + deltaData = deltaData.rowRange(0, index);
  221 + deltaLab = deltaLab.rowRange(0, index);
142 222  
143   - // Copy local file contents from stream
144   - QByteArray data;
145   - stream >> data;
  223 + trainSVM(a, b, svm, deltaData, deltaLab, kernel, type, -1, -1);
  224 + }
146 225  
147   - // Create local file
148   - QTemporaryFile tempFile(QDir::tempPath()+"/SVM");
149   - tempFile.open();
150   - tempFile.write(data);
151   - tempFile.close();
  226 + float compare(const Template &ta, const Template &tb) const
  227 + {
  228 + Mat delta;
  229 + absdiff(ta, tb, delta);
  230 + return (svm.predict(delta.reshape(1, 1)) - b)/a;
  231 + }
152 232  
153   - // Load SVM from local file
154   - svm.load(qPrintable(tempFile.fileName()));
  233 + void store(QDataStream &stream) const
  234 + {
  235 + storeSVM(a, b, svm, stream);
  236 + }
  237 +
  238 + void load(QDataStream &stream)
  239 + {
  240 + loadSVM(a, b, svm, stream);
155 241 }
156 242 };
157 243  
158   -BR_REGISTER(Transform, SVMTransform)
  244 +BR_REGISTER(Distance, SVMDistance)
159 245  
160 246 } // namespace br
161 247  
... ...
share/openbr/abstraction.svg 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8" standalone="no"?>
  2 +<!-- Created with Inkscape (http://www.inkscape.org/) -->
  3 +
  4 +<svg
  5 + xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
  6 + xmlns:dc="http://purl.org/dc/elements/1.1/"
  7 + xmlns:cc="http://creativecommons.org/ns#"
  8 + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
  9 + xmlns:svg="http://www.w3.org/2000/svg"
  10 + xmlns="http://www.w3.org/2000/svg"
  11 + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
  12 + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
  13 + width="512"
  14 + height="416"
  15 + id="svg2"
  16 + version="1.1"
  17 + inkscape:version="0.48.2 r9819"
  18 + sodipodi:docname="abstraction.svg">
  19 + <defs
  20 + id="defs4">
  21 + <linearGradient
  22 + id="linearGradient5438"
  23 + osb:paint="gradient">
  24 + <stop
  25 + style="stop-color:#bebada;stop-opacity:1;"
  26 + offset="0"
  27 + id="stop5440" />
  28 + <stop
  29 + style="stop-color:#bebada;stop-opacity:0;"
  30 + offset="1"
  31 + id="stop5442" />
  32 + </linearGradient>
  33 + </defs>
  34 + <sodipodi:namedview
  35 + id="base"
  36 + pagecolor="#ffffff"
  37 + bordercolor="#666666"
  38 + borderopacity="1.0"
  39 + inkscape:pageopacity="0.0"
  40 + inkscape:pageshadow="2"
  41 + inkscape:zoom="1.414741"
  42 + inkscape:cx="245.94939"
  43 + inkscape:cy="199.44253"
  44 + inkscape:document-units="px"
  45 + inkscape:current-layer="layer1"
  46 + showgrid="false"
  47 + inkscape:window-width="1383"
  48 + inkscape:window-height="856"
  49 + inkscape:window-x="57"
  50 + inkscape:window-y="0"
  51 + inkscape:window-maximized="1" />
  52 + <metadata
  53 + id="metadata7">
  54 + <rdf:RDF>
  55 + <cc:Work
  56 + rdf:about="">
  57 + <dc:format>image/svg+xml</dc:format>
  58 + <dc:type
  59 + rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
  60 + <dc:title />
  61 + </cc:Work>
  62 + </rdf:RDF>
  63 + </metadata>
  64 + <g
  65 + inkscape:label="Layer 1"
  66 + inkscape:groupmode="layer"
  67 + id="layer1"
  68 + transform="translate(0,-636.36218)">
  69 + <rect
  70 + style="fill:#8dd3c7;fill-opacity:1;stroke:#000000;stroke-width:1.99999988;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
  71 + id="rect5563"
  72 + width="153"
  73 + height="62"
  74 + x="342"
  75 + y="653.36218" />
  76 + <rect
  77 + style="fill:#8dd3c7;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
  78 + id="rect2985"
  79 + width="153"
  80 + height="62"
  81 + x="17"
  82 + y="653.36218" />
  83 + <rect
  84 + style="fill:#ffffb3;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
  85 + id="rect2989"
  86 + width="478"
  87 + height="62"
  88 + x="17"
  89 + y="725.36218" />
  90 + <text
  91 + xml:space="preserve"
  92 + style="font-size:40px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans Bold"
  93 + x="68.367188"
  94 + y="699.79578"
  95 + id="text2995"
  96 + sodipodi:linespacing="125%"><tspan
  97 + sodipodi:role="line"
  98 + id="tspan2997"
  99 + x="68.367188"
  100 + y="699.79578">br</tspan></text>
  101 + <text
  102 + xml:space="preserve"
  103 + style="font-size:40px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans Bold"
  104 + x="171.7876"
  105 + y="768.62451"
  106 + id="text3003"
  107 + sodipodi:linespacing="125%"><tspan
  108 + sodipodi:role="line"
  109 + id="tspan3005"
  110 + x="171.7876"
  111 + y="768.62451">OpenBR</tspan></text>
  112 + <text
  113 + xml:space="preserve"
  114 + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
  115 + x="300"
  116 + y="297.18518"
  117 + id="text3011"
  118 + sodipodi:linespacing="125%"
  119 + transform="translate(0,540.36218)"><tspan
  120 + sodipodi:role="line"
  121 + id="tspan3013"
  122 + x="300"
  123 + y="297.18518" /></text>
  124 + <text
  125 + xml:space="preserve"
  126 + style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:end;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
  127 + x="489.27899"
  128 + y="750.23242"
  129 + id="text3019"
  130 + sodipodi:linespacing="125%"><tspan
  131 + sodipodi:role="line"
  132 + id="tspan3021"
  133 + x="489.27899"
  134 + y="750.23242">OpenCV</tspan><tspan
  135 + sodipodi:role="line"
  136 + x="489.27899"
  137 + y="772.73242"
  138 + id="tspan3945">Qt</tspan></text>
  139 + <text
  140 + xml:space="preserve"
  141 + style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
  142 + x="367.25104"
  143 + y="257.92224"
  144 + id="text3836"
  145 + sodipodi:linespacing="125%"
  146 + transform="translate(0,540.36218)"><tspan
  147 + sodipodi:role="line"
  148 + id="tspan3838"
  149 + x="367.25104"
  150 + y="257.92224" /></text>
  151 + <rect
  152 + style="fill:#bebada;fill-opacity:1;stroke:#000000;stroke-width:1.99999988;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 7.99999999;stroke-dashoffset:0"
  153 + id="rect3848"
  154 + width="118"
  155 + height="62.000004"
  156 + x="377"
  157 + y="797.36218" />
  158 + <rect
  159 + style="fill:#bebada;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:3.99999988, 7.99999978;stroke-dashoffset:0"
  160 + id="rect3854"
  161 + width="62"
  162 + height="62"
  163 + x="233"
  164 + y="797.36212" />
  165 + <rect
  166 + style="fill:#bebada;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 8;stroke-dashoffset:0"
  167 + id="rect3856"
  168 + width="62"
  169 + height="62"
  170 + x="233"
  171 + y="869.36218" />
  172 + <rect
  173 + style="fill:#bebada;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 8;stroke-dashoffset:0"
  174 + id="rect3858"
  175 + width="61.999996"
  176 + height="62"
  177 + x="305"
  178 + y="797.36218" />
  179 + <text
  180 + xml:space="preserve"
  181 + style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
  182 + x="262.64453"
  183 + y="836.02234"
  184 + id="text3816"
  185 + sodipodi:linespacing="125%"><tspan
  186 + sodipodi:role="line"
  187 + id="tspan3818"
  188 + x="262.64453"
  189 + y="836.02234">PCA</tspan></text>
  190 + <text
  191 + xml:space="preserve"
  192 + style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
  193 + x="263.64453"
  194 + y="908.36218"
  195 + id="text3820"
  196 + sodipodi:linespacing="125%"><tspan
  197 + sodipodi:role="line"
  198 + id="tspan3822"
  199 + x="263.64453"
  200 + y="908.36218">LDA</tspan></text>
  201 + <text
  202 + xml:space="preserve"
  203 + style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
  204 + x="335.84473"
  205 + y="836.36218"
  206 + id="text3824"
  207 + sodipodi:linespacing="125%"><tspan
  208 + sodipodi:role="line"
  209 + id="tspan3826"
  210 + x="335.84473"
  211 + y="836.36218">LBP</tspan></text>
  212 + <rect
  213 + style="fill:#bebada;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4.00000014, 8.00000028;stroke-dashoffset:0"
  214 + id="rect3860"
  215 + width="62.000004"
  216 + height="62.000004"
  217 + x="305"
  218 + y="869.36218" />
  219 + <text
  220 + xml:space="preserve"
  221 + style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
  222 + x="336.6709"
  223 + y="914.36218"
  224 + id="text3828"
  225 + sodipodi:linespacing="125%"><tspan
  226 + sodipodi:role="line"
  227 + id="tspan3830"
  228 + x="336.6709"
  229 + y="914.36218">...</tspan></text>
  230 + <text
  231 + xml:space="preserve"
  232 + style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
  233 + x="435.84863"
  234 + y="823.69287"
  235 + id="text3840"
  236 + sodipodi:linespacing="125%"><tspan
  237 + sodipodi:role="line"
  238 + id="tspan3842"
  239 + x="435.84863"
  240 + y="823.69287"
  241 + style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Sans;-inkscape-font-specification:Sans">Commercial</tspan><tspan
  242 + sodipodi:role="line"
  243 + x="435.84863"
  244 + y="846.19287"
  245 + id="tspan5663"
  246 + style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Sans;-inkscape-font-specification:Sans">Wrapper</tspan></text>
  247 + <text
  248 + xml:space="preserve"
  249 + style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
  250 + x="419.84863"
  251 + y="678.81708"
  252 + id="text3832"
  253 + sodipodi:linespacing="125%"><tspan
  254 + sodipodi:role="line"
  255 + id="tspan3834"
  256 + x="419.84863"
  257 + y="678.81708"
  258 + style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Sans;-inkscape-font-specification:Sans">Commercial</tspan><tspan
  259 + sodipodi:role="line"
  260 + x="419.84863"
  261 + y="701.31708"
  262 + id="tspan5661"
  263 + style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Sans;-inkscape-font-specification:Sans">Application</tspan></text>
  264 + <rect
  265 + style="fill:#bebada;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
  266 + id="rect3901"
  267 + width="31.999998"
  268 + height="32"
  269 + x="288"
  270 + y="946.36218" />
  271 + <rect
  272 + style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 8;stroke-dashoffset:0"
  273 + id="rect5411"
  274 + width="30"
  275 + height="30"
  276 + x="289"
  277 + y="995.36218" />
  278 + <text
  279 + xml:space="preserve"
  280 + style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
  281 + x="326.41797"
  282 + y="972.02234"
  283 + id="text5512"
  284 + sodipodi:linespacing="125%"><tspan
  285 + sodipodi:role="line"
  286 + id="tspan5514"
  287 + x="326.41797"
  288 + y="972.02234">Source Code</tspan></text>
  289 + <text
  290 + xml:space="preserve"
  291 + style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
  292 + x="70.417969"
  293 + y="1018.37"
  294 + id="text5516"
  295 + sodipodi:linespacing="125%"><tspan
  296 + sodipodi:role="line"
  297 + id="tspan5518"
  298 + x="70.417969"
  299 + y="1018.37">Shared Library</tspan></text>
  300 + <text
  301 + xml:space="preserve"
  302 + style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
  303 + x="71.8125"
  304 + y="971.37"
  305 + id="text5520"
  306 + sodipodi:linespacing="125%"><tspan
  307 + sodipodi:role="line"
  308 + id="tspan5522"
  309 + x="71.8125"
  310 + y="971.37">Application</tspan></text>
  311 + <text
  312 + xml:space="preserve"
  313 + style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
  314 + x="325.64453"
  315 + y="1018.37"
  316 + id="text5524"
  317 + sodipodi:linespacing="125%"><tspan
  318 + sodipodi:role="line"
  319 + id="tspan5526"
  320 + x="325.64453"
  321 + y="1018.37">Plugin</tspan><tspan
  322 + sodipodi:role="line"
  323 + x="325.64453"
  324 + y="1048.37"
  325 + id="tspan5528" /></text>
  326 + <rect
  327 + style="fill:#ffffb3;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
  328 + id="rect5635"
  329 + width="32"
  330 + height="32"
  331 + x="32"
  332 + y="994.36218" />
  333 + <rect
  334 + style="fill:#8dd3c7;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
  335 + id="rect5641"
  336 + width="32"
  337 + height="32"
  338 + x="32"
  339 + y="946.36218" />
  340 + <rect
  341 + y="869.36218"
  342 + x="377"
  343 + height="62.000004"
  344 + width="118"
  345 + id="rect3923"
  346 + style="fill:#ffffb3;fill-opacity:1;stroke:#000000;stroke-width:1.99999988;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 7.99999999;stroke-dashoffset:0" />
  347 + <text
  348 + sodipodi:linespacing="125%"
  349 + id="text3925"
  350 + y="895.08405"
  351 + x="435.84863"
  352 + style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
  353 + xml:space="preserve"><tspan
  354 + style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Sans;-inkscape-font-specification:Sans"
  355 + y="895.08405"
  356 + x="435.84863"
  357 + id="tspan3927"
  358 + sodipodi:role="line">Commercial</tspan><tspan
  359 + style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Sans;-inkscape-font-specification:Sans"
  360 + id="tspan3929"
  361 + y="917.58405"
  362 + x="435.84863"
  363 + sodipodi:role="line">Library</tspan></text>
  364 + <rect
  365 + y="653.36218"
  366 + x="180"
  367 + height="62"
  368 + width="152"
  369 + id="rect3937"
  370 + style="fill:#8dd3c7;fill-opacity:1;stroke:#000000;stroke-width:1.99999988;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
  371 + <text
  372 + sodipodi:linespacing="125%"
  373 + id="text3939"
  374 + y="678.81708"
  375 + x="256.83887"
  376 + style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
  377 + xml:space="preserve"><tspan
  378 + style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Sans;-inkscape-font-specification:Sans"
  379 + y="678.81708"
  380 + x="256.83887"
  381 + id="tspan3941"
  382 + sodipodi:role="line">Open Source</tspan><tspan
  383 + style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Sans;-inkscape-font-specification:Sans"
  384 + id="tspan3943"
  385 + y="701.31708"
  386 + x="256.83887"
  387 + sodipodi:role="line">Application</tspan></text>
  388 + <rect
  389 + y="797.36218"
  390 + x="17"
  391 + height="134"
  392 + width="206"
  393 + id="rect3947"
  394 + style="fill:#bebada;fill-opacity:1;stroke:#000000;stroke-width:1.99999988;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
  395 + <text
  396 + xml:space="preserve"
  397 + style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
  398 + x="123.47915"
  399 + y="895.03503"
  400 + id="text3961"
  401 + sodipodi:linespacing="125%"><tspan
  402 + id="tspan3965"
  403 + sodipodi:role="line"
  404 + x="123.47915"
  405 + y="895.03503"
  406 + style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Sans;-inkscape-font-specification:Sans" /></text>
  407 + <text
  408 + xml:space="preserve"
  409 + style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
  410 + x="21"
  411 + y="823.81079"
  412 + id="text3993"
  413 + sodipodi:linespacing="125%"><tspan
  414 + sodipodi:role="line"
  415 + x="21"
  416 + y="823.81079"
  417 + id="tspan4025">•Algorithm evaluation</tspan><tspan
  418 + sodipodi:role="line"
  419 + x="21"
  420 + y="846.31079"
  421 + id="tspan4011">•Parallel training &amp;</tspan><tspan
  422 + sodipodi:role="line"
  423 + x="21"
  424 + y="868.81079"
  425 + id="tspan4023"> execution</tspan><tspan
  426 + sodipodi:role="line"
  427 + x="21"
  428 + y="891.31079"
  429 + id="tspan4013">•Image processing </tspan><tspan
  430 + sodipodi:role="line"
  431 + x="21"
  432 + y="913.81079"
  433 + id="tspan4015"> grammar</tspan></text>
  434 + <text
  435 + xml:space="preserve"
  436 + style="font-size:13px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#808080;fill-opacity:1;stroke:none;font-family:Sans"
  437 + x="176.38187"
  438 + y="1047.9127"
  439 + id="text4017"
  440 + sodipodi:linespacing="125%"><tspan
  441 + sodipodi:role="line"
  442 + id="tspan4019"
  443 + x="176.38187"
  444 + y="1047.9127">www.openbiometrics.org</tspan></text>
  445 + </g>
  446 +</svg>
... ...
share/openbr/openbr.bib
... ... @@ -70,21 +70,6 @@
70 70 Title = {PittPatt {SDK} 5.2.2},
71 71 Year = {2011}}
72 72  
73   -
74   -% Datasets
75   -@misc{GBU,
76   - Author = {NIST},
77   - Howpublished = {www.nist.gov/itl/iad/ig/focs.cfm},
78   - Title = {Face and Ocular Challenge Series ({FOCS})},
79   - Year = {2010}}
80   -
81   -@misc{MEDS,
82   - Author = {NIST},
83   - Howpublished = {www.nist.gov/itl/iad/ig/sd32.cfm},
84   - Title = {{NIST} Special Database 32 - Multiple Encounter Dataset ({MEDS})},
85   - Year = {2011}}
86   -
87   -
88 73 % Papers
89 74 @inproceedings{arandjelovic12,
90 75 Author={Arandjelovic, R. and Zisserman, A.},
... ... @@ -117,16 +102,22 @@
117 102 Title = {Average of Synthetic Exact Filters},
118 103 Year = {2009}}
119 104  
  105 +@misc{founds11,
  106 + Author = {Founds, A.P. and Orlans, N. and Whiddon, G. and Watson, C.},
  107 + Howpublished = {www.nist.gov/itl/iad/ig/sd32.cfm},
  108 + Title = {{NIST Special Database 32 - Multiple Encounter Dataset II (MEDS-II)}},
  109 + Year = {2011}}
  110 +
120 111 @misc{grother12,
121 112 Author = {Grother, P. and Quinn, G.W. and Ngan, M.},
122   - Title = {Face Recognition Vendor Test (FRVT) 2012},
  113 + Title = {{Face Recognition Vendor Test (FRVT)} 2012},
123 114 Month = {mar},
124 115 Year = {2013},
125 116 Url = {http://www.nist.gov/itl/iad/ig/frvt-2012.cfm}}
126 117  
127 118 @misc{grother13,
128 119 Author = {Grother, P.},
129   - Title = {{MITRE} {FRVT} {Submission} {Question}},
  120 + Title = {{MITRE FRVT Submission Question}},
130 121 HowPublished = {Personal communication},
131 122 Month = {jan},
132 123 Year = {2013}}
... ... @@ -158,7 +149,7 @@
158 149 @article{martinez98,
159 150 Author={Martinez, A.M.},
160 151 Journal={CVC Technical Report},
161   - Title={The AR face database},
  152 + Title={The {AR} face database},
162 153 Volume={24},
163 154 Year={1998}}
164 155  
... ... @@ -183,7 +174,7 @@
183 174 Booktitle = {Second international conference on audio and video-based biometric person authentication},
184 175 Organization = {Citeseer},
185 176 Pages = {965-966},
186   - Title = {XM2VTSDB: The extended M2VTS database},
  177 + Title = {{XM2VTSDB}: The extended {M2VTS} database},
187 178 Volume = {964},
188 179 Year = {1999}}
189 180  
... ... @@ -224,7 +215,7 @@
224 215 Author = {Li, S.Z. and Lei, Z. and Ao, M.},
225 216 Booktitle = {6th IEEE Workshop on Object Tracking and Classification Beyond and in the Visible Spectrum (OTCBVS, in conjunction with CVPR 2009)},
226 217 Month = {jun},
227   - Title = {The HFB Face Database for Heterogeneous Face Biometrics Research},
  218 + Title = {{The HFB Face Database} for {Heterogeneous Face Biometrics} Research},
228 219 Year = {2009}}
229 220  
230 221 @article{wang09,
... ...