Commit d0f1b236473da269610f25b7a9ecc6ff754c6754

Authored by Charles Otto
2 parents 9670e296 ead4a56a

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

CHANGELOG.md
1 1 0.4.0 - ??/??/??
2 2 ================
  3 +* Added -evalLandmarking and -plotLandmarking for evaluating and plotting landmarking accuracy (#9)
3 4 * Added -evalDetection and -plotDetection for evaluating and plotting object detection accuracy (#9)
4 5 * Deprecated Transform::backProject
5 6  
... ...
app/br/br.cpp
... ... @@ -146,6 +146,9 @@ public:
146 146 } else if (!strcmp(fun, "plotDetection")) {
147 147 check(parc >= 2, "Incorrect parameter count for 'plotDetection'.");
148 148 br_plot_detection(parc-1, parv, parv[parc-1], true);
  149 + } else if (!strcmp(fun, "plotLandmarking")) {
  150 + check(parc >= 2, "Incorrect parameter count for 'plotLandmarking'.");
  151 + br_plot_landmarking(parc-1, parv, parv[parc-1], true);
149 152 } else if (!strcmp(fun, "plotMetadata")) {
150 153 check(parc >= 2, "Incorrect parameter count for 'plotMetadata'.");
151 154 br_plot_metadata(parc-1, parv, parv[parc-1], true);
... ... @@ -224,6 +227,7 @@ private:
224 227 "-evalLandmarking <predicted_gallery> <truth_gallery> [{csv} [<normalization_index_a> <normalization_index_b>]]\n"
225 228 "-evalRegression <predicted_gallery> <truth_gallery> <predicted property name> <ground truth property name>\n"
226 229 "-plotDetection <file> ... <file> {destination}\n"
  230 + "-plotLandmarking <file> ... <file> {destination}\n"
227 231 "-plotMetadata <file> ... <file> <columns>\n"
228 232 "-getHeader <matrix>\n"
229 233 "-setHeader {<matrix>} <target_gallery> <query_gallery>\n"
... ...
data/FDDB/README.md 0 → 100644
  1 +## FDDB Face Detection Data Set and Benchmark
  2 +Unconstrained face detection dataset containing 2845 images with 5171 annotated face locations. Contained in this directory are .FDDB files for train and test splits for FDDB. These are based on the `FDDB-folds` directory that comes with the original dataset, but organized as 10 different train/test splits instead of 10 folds. All splits are based on the original folds.
  3 +
  4 +Image data currently available online.
  5 +* [Website](http://vis-www.cs.umass.edu/fddb/)
... ...
data/README.md
... ... @@ -10,6 +10,7 @@
10 10 * [MNIST](MNIST/README.md)
11 11 * [PCSO](PCSO/README.md)
12 12 * [KTH](KTH/README.md)
  13 +* [FDDB](FDDB/README.md)
13 14  
14 15 For both practical and legal reasons we only include images for some of the datasets in this repository.
15 16 Researchers should contact the respective owners of the other datasets in order to obtain a copy.
... ...
openbr/core/plot.cpp
... ... @@ -228,7 +228,7 @@ bool Plot(const QStringList &amp;files, const File &amp;destination, bool show)
228 228 QString(", xlab=\"False Accept Rate\", ylab=\"True Accept Rate\") + theme_minimal()") +
229 229 (p.major.size > 1 ? getScale("colour", p.major.header, p.major.size) : QString()) +
230 230 (p.minor.size > 1 ? QString(" + scale_linetype_discrete(\"%1\")").arg(p.minor.header) : QString()) +
231   - QString(" + scale_x_log10(labels=percent) + scale_y_continuous(labels=percent) + annotation_logticks(sides=\"b\")\n\n")));
  231 + QString(" + scale_x_log10(labels=percent, limits=c(min(DET$X),1)) + scale_y_continuous(labels=percent) + annotation_logticks(sides=\"b\")\n\n")));
232 232  
233 233 p.file.write(qPrintable(QString("qplot(X, Y, data=DET%1").arg((p.major.smooth || p.minor.smooth) ? ", geom=\"smooth\", method=loess, level=0.99" : ", geom=\"line\"") +
234 234 (p.major.size > 1 ? QString(", colour=factor(%1)").arg(p.major.header) : QString()) +
... ... @@ -236,7 +236,7 @@ bool Plot(const QStringList &amp;files, const File &amp;destination, bool show)
236 236 QString(", xlab=\"False Accept Rate\", ylab=\"False Reject Rate\") + geom_abline(alpha=0.5, colour=\"grey\", linetype=\"dashed\") + theme_minimal()") +
237 237 (p.major.size > 1 ? getScale("colour", p.major.header, p.major.size) : QString()) +
238 238 (p.minor.size > 1 ? QString(" + scale_linetype_discrete(\"%1\")").arg(p.minor.header) : QString()) +
239   - QString(" + scale_x_log10(labels=percent) + scale_y_log10(labels=percent) + annotation_logticks()\n\n")));
  239 + QString(" + scale_x_log10(labels=percent, limits=c(min(DET$X),1)) + scale_y_log10(labels=percent) + annotation_logticks()\n\n")));
240 240  
241 241 p.file.write(qPrintable(QString("qplot(X, data=SD, geom=\"histogram\", fill=Y, position=\"identity\", alpha=I(1/2)") +
242 242 QString(", xlab=\"Score%1\"").arg((p.flip ? p.major.size : p.minor.size) > 1 ? " / " + (p.flip ? p.major.header : p.minor.header) : QString()) +
... ... @@ -302,7 +302,6 @@ bool filesHaveSinglePoint(const QStringList &amp;files) {
302 302 bool PlotDetection(const QStringList &files, const File &destination, bool show)
303 303 {
304 304 qDebug("Plotting %d detection file(s) to %s", files.size(), qPrintable(destination));
305   -
306 305 RPlot p(files, destination, false);
307 306  
308 307 p.file.write("# Split data into individual plots\n"
... ... @@ -345,6 +344,27 @@ bool PlotDetection(const QStringList &amp;files, const File &amp;destination, bool show)
345 344 return p.finalize(show);
346 345 }
347 346  
  347 +bool PlotLandmarking(const QStringList &files, const File &destination, bool show)
  348 +{
  349 + qDebug("Plotting %d landmarking file(s) to %s", files.size(), qPrintable(destination));
  350 + RPlot p(files, destination, false);
  351 +
  352 + p.file.write("# Split data into individual plots\n"
  353 + "plot_index = which(names(data)==\"Plot\")\n"
  354 + "Box <- data[grep(\"Box\",data$Plot),-c(1)]\n"
  355 + "rm(data)\n"
  356 + "\n");
  357 +
  358 + p.file.write(qPrintable(QString("ggplot(Box, aes(Y,%1%2))").arg(p.major.size > 1 ? QString(", colour=%1").arg(p.major.header) : QString(), p.minor.size > 1 ? QString(", linetype=%1").arg(p.minor.header) : QString()) +
  359 + QString(" + annotation_logticks(sides=\"b\") + stat_ecdf() + scale_x_log10(\"Normalized Error\", breaks=c(0.001,0.01,0.1,1,10)) + scale_y_continuous(\"Cumulative Density\", label=percent) + theme_minimal()\n\n")));
  360 + p.file.write(qPrintable(QString("ggplot(Box, aes(factor(X), Y%1%2))").arg(p.major.size > 1 ? QString(", colour=%1").arg(p.major.header) : QString(), p.minor.size > 1 ? QString(", linetype=%1").arg(p.minor.header) : QString()) +
  361 + QString("+ annotation_logticks(sides=\"l\") + geom_boxplot(alpha=0.5) + geom_jitter(size=1, alpha=0.5) + scale_x_discrete(\"Landmark\") + scale_y_log10(\"Normalized Error\", breaks=c(0.01,0.1,1,10)) + theme_minimal()\n\n")));
  362 + p.file.write(qPrintable(QString("ggplot(Box, aes(factor(X), Y%1%2))").arg(p.major.size > 1 ? QString(", colour=%1").arg(p.major.header) : QString(), p.minor.size > 1 ? QString(", linetype=%1").arg(p.minor.header) : QString()) +
  363 + QString("+ annotation_logticks(sides=\"l\") + geom_violin(alpha=0.5) + scale_x_discrete(\"Landmark\") + scale_y_log10(\"Normalized Error\", breaks=c(0.001,0.01,0.1,1,10))\n\n")));
  364 +
  365 + return p.finalize(show);
  366 +}
  367 +
348 368 bool PlotMetadata(const QStringList &files, const QString &columns, bool show)
349 369 {
350 370 qDebug("Plotting %d metadata file(s) for columns %s", files.size(), qPrintable(columns));
... ...
openbr/core/plot.h
... ... @@ -26,6 +26,7 @@ namespace br
26 26 {
27 27 bool Plot(const QStringList &files, const File &destination, bool show = false);
28 28 bool PlotDetection(const QStringList &files, const File &destination, bool show = false);
  29 + bool PlotLandmarking(const QStringList &files, const File &destination, bool show = false);
29 30 bool PlotMetadata(const QStringList &files, const QString &destination, bool show = false);
30 31 }
31 32  
... ...
openbr/core/resource.h
... ... @@ -40,6 +40,9 @@ class DefaultResourceMaker : public ResourceMaker&lt;T&gt;
40 40 T *make() const { return new T(); }
41 41 };
42 42  
  43 +// Manage multiple copies of a limited resource in a thread-safe manner.
  44 +// TimeVaryingTransform makes a strong assumption that ResourceMaker::Make
  45 +// is only called in acquire, not in the constructor.
43 46 template <typename T>
44 47 class Resource
45 48 {
... ...
openbr/openbr.cpp
... ... @@ -182,6 +182,11 @@ bool br_plot_detection(int num_files, const char *files[], const char *destinati
182 182 return PlotDetection(QtUtils::toStringList(num_files, files), destination, show);
183 183 }
184 184  
  185 +bool br_plot_landmarking(int num_files, const char *files[], const char *destination, bool show)
  186 +{
  187 + return PlotLandmarking(QtUtils::toStringList(num_files, files), destination, show);
  188 +}
  189 +
185 190 bool br_plot_metadata(int num_files, const char *files[], const char *columns, bool show)
186 191 {
187 192 return PlotMetadata(QtUtils::toStringList(num_files, files), columns, show);
... ...
openbr/openbr.h
... ... @@ -265,6 +265,12 @@ BR_EXPORT const char *br_objects(const char *abstractions = &quot;.*&quot;, const char *im
265 265 * - <i>destination</i><tt>.R</tt> which is the auto-generated R script used to render the figures.
266 266 * - <i>destination</i><tt>.pdf</tt> which has all of the figures in one file multi-page file.
267 267 *
  268 + * OpenBR uses file and folder names to automatically determine the plot legend.
  269 + * For example, let's consider the case where three algorithms (<tt>A</tt>, <tt>B</tt>, & <tt>C</tt>) were each evaluated on two datasets (<tt>Y</tt> & <tt>Z</tt>).
  270 + * The suggested way to plot these experiments on the same graph is to create a folder named <tt>Algorithm_Dataset</tt> that contains the six <tt>.csv</tt> files produced by \ref br_eval: <tt>A_Y.csv</tt>, <tt>A_Z.csv</tt>, <tt>B_Y.csv</tt>, <tt>B_Z.csv</tt>, <tt>C_Y.csv</tt>, & <tt>C_Z.csv</tt>.
  271 + * The '<tt>_</tt>' character plays a special role in determining the legend title(s) and value(s).
  272 + * In this case, <tt>A</tt>, <tt>B</tt>, & <tt>C</tt> will be identified as different values of type <tt>Algorithm</tt>, and each will be assigned its own color; <tt>Y</tt> & <tt>Z</tt> will be identified as different values of type Dataset, and each will be assigned its own line style.
  273 + *
268 274 * \param num_files Number of <tt>.csv</tt> files.
269 275 * \param files <tt>.csv</tt> files created using \ref br_eval.
270 276 * \param destination Basename for the resulting figures.
... ... @@ -295,6 +301,20 @@ BR_EXPORT bool br_plot(int num_files, const char *files[], const char *destinati
295 301 BR_EXPORT bool br_plot_detection(int num_files, const char *files[], const char *destination, bool show = false);
296 302  
297 303 /*!
  304 + * \brief Renders landmarking performance figures for a set of <tt>.csv</tt> files created by \ref br_eval_landmarking.
  305 + *
  306 + * In order of their output, the figures are:
  307 + * -# Cumulative landmarks less than normalized error (CD)
  308 + * -# Normalized error box and whisker plots (Box)
  309 + * -# Normalized error violin plots (Violin)
  310 + *
  311 + * Landmarking error is normalized against the distance between two predifined points, usually inter-ocular distance (IOD).
  312 + *
  313 + * \see br_plot
  314 + */
  315 +BR_EXPORT bool br_plot_landmarking(int num_files, const char *files[], const char *destination, bool show = false);
  316 +
  317 +/*!
298 318 * \brief Renders metadata figures for a set of <tt>.csv</tt> files with specified columns.
299 319 *
300 320 * Several files will be created:
... ...
openbr/plugins/algorithms.cpp
... ... @@ -50,9 +50,9 @@ class AlgorithmsInitializer : public Initializer
50 50 Globals->abbreviations.insert("PerFrameDetection", "Stream(SaveMat(original)+Cvt(Gray)+Cascade(FrontalFace)+ASEFEyes+RestoreMat(original)+Draw(inPlace=true)+Show(false,[FrameNumber])+Discard)");
51 51 Globals->abbreviations.insert("AgeGenderDemo", "Stream(SaveMat(original)+Cvt(Gray)+Cascade(FrontalFace)+Expand+<FaceClassificationRegistration>+<FaceClassificationExtraction>+<AgeRegressor>/<GenderClassifier>+Discard+RestoreMat(original)+Draw(inPlace=true)+DrawPropertiesPoint([Age,Gender],Affine_0,inPlace=true)+SaveMat(original)+Discard+Contract+RestoreMat(original)+FPSCalc+Show(false,[AvgFPS,Age,Gender])+Discard)");
52 52  
53   - Globals->abbreviations.insert("HOG", "Stream(DropFrames(5)+Cvt(Gray)+KeyPointDetector(SIFT)+ROI+Expand+Resize(32,32)+Gradient+RectRegions+Bin(0,360,8)+Hist(8)+Cat)+Contract+CatRows+KMeans(500)+Hist(500)+SVM");
54   - Globals->abbreviations.insert("HOF", "Stream(DropFrames(5)+KeyPointDetector(SIFT)+AggregateFrames(2)+OpticalFlow+ROI+Expand+Resize(32,32)+Gradient+RectRegions+Bin(0,360,8)+Hist(8)+Cat)+Contract+CatRows+KMeans(500)+Hist(500)");
55   - Globals->abbreviations.insert("HOGHOF", "Stream(DropFrames(5)+KeyPointDetector(SIFT)+AggregateFrames(2)+(OpticalFlow++ROI+Expand+Resize(32,32)+Gradient+RectRegions+Bin(0,360,8)+Hist(8)+Cat+Contract)/(First+Cvt(Gray)+ROI+Expand+Resize(32,32)+Gradient+RectRegions+Bin(0,360,8)+Hist(8)+Cat+Contract)+CatCols)+Contract+CatRows+KMeans(500)+Hist(500)+SVM");
  53 + Globals->abbreviations.insert("HOG", "Stream(DropFrames(5)+Cvt(Gray)+Grid(5,5)+ROIFromPts(32,24)+Expand+Resize(32,32)+Gradient+RectRegions+Bin(0,360,8)+Hist(8)+Cat)+Contract+CatRows+KMeans(500)+Hist(500)+SVM");
  54 + Globals->abbreviations.insert("HOF", "Stream(DropFrames(5)+Grid(5,5)+AggregateFrames(2)+OpticalFlow+ROIFromPts(32,24)+Expand+Resize(32,32)+Gradient+RectRegions+Bin(0,360,8)+Hist(8)+Cat)+Contract+CatRows+KMeans(500)+Hist(500)");
  55 + Globals->abbreviations.insert("HOGHOF", "Stream(DropFrames(5)+Grid(5,5)+AggregateFrames(2)+(OpticalFlow+ROIFromPts(32,24)+Expand+Resize(32,32)+Gradient+RectRegions+Bin(0,360,8)+Hist(8)+Cat+Contract)/(First+Cvt(Gray)+ROIFromPts(32,24)+Expand+Resize(32,32)+Gradient+RectRegions+Bin(0,360,8)+Hist(8)+Cat+Contract)+CatCols)+Contract+CatRows+KMeans(500)+Hist(500)+SVM");
56 56  
57 57 // Generic Image Processing
58 58 Globals->abbreviations.insert("SIFT", "Open+KeyPointDetector(SIFT)+KeyPointDescriptor(SIFT):KeyPointMatcher(BruteForce)");
... ...
openbr/plugins/crop.cpp
... ... @@ -69,6 +69,31 @@ BR_REGISTER(Transform, ROITransform)
69 69  
70 70 /*!
71 71 * \ingroup transforms
  72 + * \brief Crops the rectangular regions of interest from given points and sizes.
  73 + * \author Austin Blanton \cite imaus10
  74 + */
  75 +class ROIFromPtsTransform : public UntrainableTransform
  76 +{
  77 + Q_OBJECT
  78 + Q_PROPERTY(int width READ get_width WRITE set_width RESET reset_width STORED false)
  79 + Q_PROPERTY(int height READ get_height WRITE set_height RESET reset_height STORED false)
  80 + BR_PROPERTY(int, width, 1)
  81 + BR_PROPERTY(int, height, 1)
  82 +
  83 + void project(const Template &src, Template &dst) const
  84 + {
  85 + foreach (const QPointF &pt, src.file.points()) {
  86 + int x = pt.x() - (width/2);
  87 + int y = pt.y() - (height/2);
  88 + dst += src.m()(Rect(x, y, width, height));
  89 + }
  90 + }
  91 +};
  92 +
  93 +BR_REGISTER(Transform, ROIFromPtsTransform)
  94 +
  95 +/*!
  96 + * \ingroup transforms
72 97 * \brief Resize the template
73 98 * \author Josh Klontz \cite jklontz
74 99 * \note Method: Area should be used for shrinking an image, Cubic for slow but accurate enlargment, Bilin for fast enlargement.
... ...
openbr/plugins/eigen3.cpp
... ... @@ -253,9 +253,7 @@ class DFFSTransform : public Transform
253 253 {
254 254 Q_OBJECT
255 255 Q_PROPERTY(float keep READ get_keep WRITE set_keep RESET reset_keep STORED false)
256   - Q_PROPERTY(br::Transform *transform READ get_transform WRITE set_transform STORED false)
257 256 BR_PROPERTY(float, keep, 0.95)
258   - BR_PROPERTY(br::Transform*, transform, NULL)
259 257  
260 258 PCATransform pca;
261 259 Transform *cvtFloat;
... ...
openbr/plugins/fst3.cmake deleted
1   -set(BR_WITH_FST3 OFF CACHE BOOL "Build with Feature Selection Toolbox 3")
2   -
3   -if(${BR_WITH_FST3})
4   - find_package(FST3 REQUIRED)
5   - set(BR_THIRDPARTY_LIBS ${BR_THIRDPARTY_LIBS} plugins/fst3.cpp ${FST3_SRC})
6   -
7   - find_package(Boost REQUIRED)
8   - include_directories(${Boost_INCLUDE_DIRS})
9   - set(BR_THIRDPARTY_LIBS ${BR_THIRDPARTY_LIBS} boost_thread)
10   -
11   - find_package(LibSVM REQUIRED)
12   - set(BR_THIRDPARTY_SRC ${BR_THIRDPARTY_SRC} ${LibSVM_SRC})
13   -endif()
openbr/plugins/fst3.cpp deleted
1   -#include <QMap>
2   -#include <QString>
3   -#include <QStringList>
4   -#include <QTime>
5   -#include <opencv2/core/core.hpp>
6   -#include <opencv2/imgproc/imgproc.hpp>
7   -#include <mm_plugin.h>
8   -
9   -#include "model.h"
10   -#include "common/opencvutils.h"
11   -#include "common/qtutils.h"
12   -#include "plugins/meta.h"
13   -#include "plugins/regions.h"
14   -
15   -//#ifdef MM_SDK_TRAINABLE
16   -#include <boost/smart_ptr.hpp>
17   -#include <exception>
18   -#include <iostream>
19   -#include <cstdlib>
20   -#include <sstream>
21   -#include <string>
22   -#include <vector>
23   -
24   -#include <error.hpp>
25   -#include <global.hpp>
26   -#include <subset.hpp>
27   -#include <data_intervaller.hpp>
28   -#include <data_splitter.hpp>
29   -#include <data_splitter_5050.hpp>
30   -#include <data_splitter_cv.hpp>
31   -#include <data_splitter_resub.hpp>
32   -#include <data_scaler.hpp>
33   -#include <data_scaler_void.hpp>
34   -#include <data_accessor_splitting_mem.hpp>
35   -#include <criterion_wrapper.hpp>
36   -#include <distance_euclid.hpp>
37   -#include <classifier_knn.hpp>
38   -#include <seq_step_straight_threaded.hpp>
39   -#include <search_seq_dos.hpp>
40   -#include <search_seq_sfs.hpp>
41   -#include <search_seq_sffs.hpp>
42   -#include <search_monte_carlo_threaded.hpp>
43   -
44   -using namespace FST;
45   -//#endif // MM_SDK_TRAINABLE
46   -
47   -using namespace mm;
48   -
49   -enum DimensionStatus {
50   - On,
51   - Off,
52   - Ignore
53   -};
54   -
55   -//#ifdef MM_SDK_TRAINABLE
56   -template<typename DATATYPE, typename IDXTYPE, class INTERVALCONTAINER>
57   -class FST3Data_Accessor_Splitting_MemMM : public Data_Accessor_Splitting_Mem<DATATYPE,IDXTYPE,INTERVALCONTAINER>
58   -{
59   - QList<MatrixList> mll;
60   - QList<DimensionStatus> dsl;
61   - int features;
62   - QMap<int, int> labelCounts;
63   -
64   -public:
65   - typedef Data_Accessor_Splitting_Mem<DATATYPE,IDXTYPE,INTERVALCONTAINER> DASM;
66   - typedef boost::shared_ptr<Data_Scaler<DATATYPE> > PScaler;
67   - typedef typename DASM::PSplitters PSplitters;
68   -
69   - FST3Data_Accessor_Splitting_MemMM(const QList<MatrixList> &_mll, const QList<DimensionStatus> &_dsl, const PSplitters _dsp, const PScaler _dsc)
70   - : Data_Accessor_Splitting_Mem<DATATYPE,IDXTYPE,INTERVALCONTAINER>("MM", _dsp, _dsc), mll(_mll), dsl(_dsl)
71   - {
72   - features = 0;
73   - foreach (DimensionStatus ds, dsl)
74   - if (ds != Ignore) features++;
75   - labelCounts = mll.first().labelCounts();
76   - }
77   -
78   - FST3Data_Accessor_Splitting_MemMM(const MatrixList &_ml, const PSplitters _dsp, const PScaler _dsc)
79   - : Data_Accessor_Splitting_Mem<DATATYPE,IDXTYPE,INTERVALCONTAINER>("MM", _dsp, _dsc)
80   - {
81   - mll.append(_ml);
82   - features = _ml.first().total() * _ml.first().channels();
83   - for (int i=0; i<features; i++)
84   - dsl.append(Off);
85   - labelCounts = _ml.labelCounts();
86   - }
87   -
88   - FST3Data_Accessor_Splitting_MemMM* sharing_clone() const;
89   - virtual std::ostream& print(std::ostream& os) const;
90   -
91   -protected:
92   - FST3Data_Accessor_Splitting_MemMM(const Data_Accessor_Splitting_MemMM &damt, int x)
93   - : Data_Accessor_Splitting_Mem<DATATYPE,IDXTYPE,INTERVALCONTAINER>(damt, x)
94   - {} // weak (referencing) copy-constructor to be used in sharing_clone()
95   -
96   - virtual void initial_data_read(); //!< \note off-limits in shared_clone
97   - virtual void initial_file_prepare() {}
98   -
99   -public:
100   - virtual unsigned int file_getNoOfClasses() const { return labelCounts.size(); }
101   - virtual unsigned int file_getNoOfFeatures() const { return features; }
102   - virtual IDXTYPE file_getClassSize(unsigned int cls) const { return labelCounts[cls]; }
103   -};
104   -
105   -template<typename DATATYPE, typename IDXTYPE, class INTERVALCONTAINER>
106   -void FST3Data_Accessor_Splitting_MemMM<DATATYPE,IDXTYPE,INTERVALCONTAINER>::initial_data_read() //!< \note off-limits in shared_clone
107   -{
108   - if (Clonable::is_sharing_clone()) throw fst_error("Data_Accessor_Splitting_MemMM()::initial_data_read() called from shared_clone instance.");
109   - IDXTYPE idx=0;
110   -
111   - // TODO: Assert that ml data type is DATATYPE
112   - const QList<float> labels = mll.first().labels();
113   - foreach (int label, labelCounts.keys()) {
114   - for (int i=0; i<labels.size(); i++) {
115   - if (labels[i] == label) {
116   - int dslIndex = 0;
117   - foreach (const MatrixList &ml, mll) {
118   - const Matrix &m = ml[i];
119   - const int dims = m.total() * m.channels();
120   - for (int j=0; j<dims; j++)
121   - if (dsl[dslIndex++] != Ignore)
122   - this->data[idx++] = reinterpret_cast<float*>(m.data)[j];
123   - }
124   - }
125   - }
126   - }
127   -}
128   -
129   -/*template<typename DATATYPE, typename IDXTYPE, class INTERVALCONTAINER>
130   -Data_Accessor_Splitting_MemMM<DATATYPE,IDXTYPE,INTERVALCONTAINER>* Data_Accessor_Splitting_MemMM<DATATYPE,IDXTYPE,INTERVALCONTAINER>::sharing_clone() const
131   -{
132   - Data_Accessor_Splitting_MemMM<DATATYPE,IDXTYPE,INTERVALCONTAINER> *clone=new Data_Accessor_Splitting_MemMM<DATATYPE,IDXTYPE,INTERVALCONTAINER>(*this, (int)0);
133   - clone->set_sharing_cloned();
134   - return clone;
135   -}
136   -
137   -template<typename DATATYPE, typename IDXTYPE, class INTERVALCONTAINER>
138   -std::ostream& Data_Accessor_Splitting_MemMM<DATATYPE,IDXTYPE,INTERVALCONTAINER>::print(std::ostream& os) const
139   -{
140   - DASM::print(os);
141   - os << std::endl << "Data_Accessor_Splitting_MemMM()";
142   - return os;
143   -}*/
144   -
145   -//#endif // MM_SDK_TRAINABLE
146   -
147   -
148   -class FST3DOS : public Feature
149   -{
150   - friend class Maker<DOS,true>;
151   -
152   - int delta;
153   -
154   - mm::Remap remap;
155   -
156   - DOS(int delta = 1)
157   - {
158   - this->delta = delta;
159   - }
160   -
161   - static QString args()
162   - {
163   - return "delta = 1";
164   - }
165   -
166   - static DOS *make(const QString &args)
167   - {
168   - QStringList words = QtUtils::parse(args);
169   - if (words.size() > 1) qFatal("DOS::make invalid argument count.");
170   -
171   - int delta = 1;
172   -
173   - bool ok;
174   - switch (words.size()) {
175   - case 1:
176   - delta = words[0].toInt(&ok); if (!ok) qFatal("DOS::make expected integer delta.");
177   - }
178   -
179   - return new DOS(delta);
180   - }
181   -
182   - QSharedPointer<Feature> clone() const
183   - {
184   - return QSharedPointer<Feature>(new DOS(delta));
185   - }
186   -
187   - void train(const MatrixList &data, Matrix &metadata)
188   - {
189   - (void) metadata;
190   - //#ifdef MM_SDK_TRAINABLE
191   - try {
192   - typedef float RETURNTYPE; typedef float DATATYPE; typedef float REALTYPE;
193   - typedef unsigned int IDXTYPE; typedef unsigned int DIMTYPE; typedef int BINTYPE;
194   - typedef Subset<BINTYPE, DIMTYPE> SUBSET;
195   - typedef Data_Intervaller<std::vector<Data_Interval<IDXTYPE> >,IDXTYPE> INTERVALLER;
196   - typedef boost::shared_ptr<Data_Splitter<INTERVALLER,IDXTYPE> > PSPLITTER;
197   - typedef Data_Splitter_CV<INTERVALLER,IDXTYPE> SPLITTERCV;
198   - typedef Data_Splitter_5050<INTERVALLER,IDXTYPE> SPLITTER5050;
199   - typedef Data_Splitter_Resub<INTERVALLER,IDXTYPE> SPLITTERRESUB;
200   - typedef Data_Accessor_Splitting_MemMM<DATATYPE,IDXTYPE,INTERVALLER> DATAACCESSOR;
201   - typedef Distance_Euclid<DATATYPE,DIMTYPE,SUBSET> DISTANCE;
202   - typedef Classifier_kNN<RETURNTYPE,DATATYPE,IDXTYPE,DIMTYPE,SUBSET,DATAACCESSOR,DISTANCE> CLASSIFIERKNN;
203   - typedef Criterion_Wrapper<RETURNTYPE,SUBSET,CLASSIFIERKNN,DATAACCESSOR> WRAPPERKNN;
204   - typedef Sequential_Step_Straight_Threaded<RETURNTYPE,DIMTYPE,SUBSET,WRAPPERKNN,24> EVALUATOR;
205   -
206   - // Initialize dataset
207   - PSPLITTER dsp_outer(new SPLITTER5050()); // keep second half of data for independent testing of final classification performance
208   - PSPLITTER dsp_inner(new SPLITTERCV(3)); // in the course of search use the first half of data by 3-fold cross-validation in wrapper FS criterion evaluation
209   - boost::shared_ptr<Data_Scaler<DATATYPE> > dsc(new Data_Scaler_void<DATATYPE>()); // do not scale data
210   - boost::shared_ptr<std::vector<PSPLITTER> > splitters(new std::vector<PSPLITTER>); // set-up data access
211   - splitters->push_back(dsp_outer); //splitters->push_back(dsp_inner);
212   - boost::shared_ptr<DATAACCESSOR> da(new DATAACCESSOR(data, splitters, dsc));
213   - da->initialize();
214   - da->setSplittingDepth(0); if(!da->getFirstSplit()) throw fst_error("50/50 data split failed.");
215   - //da->setSplittingDepth(1); if(!da->getFirstSplit()) throw fst_error("3-fold cross-validation failure.");
216   - boost::shared_ptr<SUBSET> sub(new SUBSET(da->getNoOfFeatures())); // initiate the storage for subset to-be-selected
217   - //sub->select_all();
218   -
219   - // Run search
220   - boost::shared_ptr<CLASSIFIERKNN> cknn(new CLASSIFIERKNN); cknn->set_k(1);
221   - boost::shared_ptr<WRAPPERKNN> wknn(new WRAPPERKNN);
222   - wknn->initialize(cknn,da);
223   - boost::shared_ptr<EVALUATOR> eval(new EVALUATOR); // set-up the standard sequential search step object (option: hybrid, ensemble, etc.)
224   - //Search_DOS<RETURNTYPE,DIMTYPE,SUBSET,WRAPPERKNN,EVALUATOR> srch(eval); // set-up Sequential Forward Floating Selection search procedure
225   - //srch.set_delta(delta);
226   -
227   - //FST::Search_SFFS<RETURNTYPE,DIMTYPE,SUBSET,WRAPPERKNN,EVALUATOR> srch(eval);
228   - //srch.set_search_direction(FST::BACKWARD);
229   -
230   - //FST::Search_SFS<RETURNTYPE,DIMTYPE,SUBSET,WRAPPERKNN,EVALUATOR> srch(eval);
231   - //srch.set_search_direction(FST::FORWARD);
232   -
233   - FST::Search_Monte_Carlo_Threaded<RETURNTYPE,DIMTYPE,SUBSET,WRAPPERKNN,24> srch;
234   - srch.set_cardinality_randomization(0.5); // probability of inclusion of each particular feature (~implies also the expected subset size)
235   - srch.set_stopping_condition(0/*max trials*/,30/*seconds*/); // one or both values must have positive value
236   -
237   - RETURNTYPE critval_train;
238   - if(!srch.search(0,critval_train,sub,wknn,std::cout)) throw fst_error("Search not finished.");
239   -
240   - // Create map matrix
241   - const int dims = sub->get_d_raw();
242   - cv::Mat xMap(1, dims, CV_16SC1),
243   - yMap(1, dims, CV_16SC1);
244   - int index = 0;
245   - for (int i=0; i<dims; i++) {
246   - if (sub->selected_raw(i)) {
247   - xMap.at<short>(0, index) = i;
248   - yMap.at<short>(0, index) = 0;
249   - index++;
250   - }
251   - }
252   -
253   - remap = Remap(xMap, yMap, cv::INTER_NEAREST);
254   - }
255   - catch (fst_error &e) { qFatal("FST ERROR: %s, code=%d", e.what(), e.code()); }
256   - catch (std::exception &e) { qFatal("non-FST ERROR: %s", e.what()); }
257   - metadata >> remap;
258   - //#else // MM_SDK_TRAINABLE
259   - //qFatal("StreamwiseFS::train not supported.");
260   - //#endif // MM_SDK_TRAINABLE
261   - }
262   -
263   - void project(const Matrix &src, Matrix &dst) const
264   - {
265   - dst = src;
266   - dst >> remap;
267   - }
268   -
269   - void store(QDataStream &stream) const
270   - {
271   - stream << remap;
272   - }
273   -
274   - void load(QDataStream &stream)
275   - {
276   - stream >> remap;
277   - }
278   -};
279   -
280   -MM_REGISTER(Feature, FST3DOS, true)
281   -
282   -
283   -class FST3StreamwiseFS : public Feature
284   -{
285   - friend class Maker<StreamwiseFS,true>;
286   -
287   - QSharedPointer<Feature> weakLearnerTemplate;
288   - int time;
289   -
290   - mm::Dup dup;
291   - mm::Remap remap;
292   -
293   - StreamwiseFS(const QSharedPointer<Feature> &weakLearnerTemplate, int time)
294   - : dup(weakLearnerTemplate, 1)
295   - {
296   - this->weakLearnerTemplate = weakLearnerTemplate;
297   - this->time = time;
298   - }
299   -
300   - static QString args()
301   - {
302   - return "<feature> weakLearnerTemplate, int time";
303   - }
304   -
305   - static StreamwiseFS *make(const QString &args)
306   - {
307   - QStringList words = QtUtils::parse(args);
308   - if (words.size() != 2) qFatal("StreamwiseFS::make invalid argument count.");
309   -
310   - QSharedPointer<Feature> weakLearnerTemplate = Feature::make(words[0]);
311   - bool ok;
312   - int time = words[1].toInt(&ok); assert(ok);
313   -
314   - return new StreamwiseFS(weakLearnerTemplate, time);
315   - }
316   -
317   - QSharedPointer<Feature> clone() const
318   - {
319   - return QSharedPointer<Feature>(new StreamwiseFS(weakLearnerTemplate, time));
320   - }
321   -
322   - void train(const MatrixList &data, Matrix &metadata)
323   - {
324   - QList< QSharedPointer<Feature> > weakLearners;
325   - QList<MatrixList> projectedDataList;
326   - QList<int> weakLearnerDimsList;
327   - QList<DimensionStatus> dimStatusList;
328   -
329   - QTime timer; timer.start();
330   - while (timer.elapsed() / 1000 < time) {
331   - // Construct a new weak learner
332   - QSharedPointer<Feature> newWeakLearner = weakLearnerTemplate->clone();
333   - Matrix metadataCopy(metadata);
334   - newWeakLearner->train(data, metadataCopy);
335   - weakLearners.append(newWeakLearner);
336   -
337   - MatrixList projectedData = data;
338   - projectedData >> *newWeakLearner;
339   - projectedDataList.append(projectedData);
340   - weakLearnerDimsList.append(projectedData.first().total() * projectedData.first().channels());
341   - for (int i=0; i<weakLearnerDimsList.last(); i++) dimStatusList.append(Off);
342   -
343   - //#ifdef MM_SDK_TRAINABLE
344   - try
345   - {
346   - typedef float RETURNTYPE; typedef float DATATYPE; typedef float REALTYPE;
347   - typedef unsigned int IDXTYPE; typedef unsigned int DIMTYPE; typedef int BINTYPE;
348   - typedef Subset<BINTYPE, DIMTYPE> SUBSET;
349   - typedef Data_Intervaller<std::vector<Data_Interval<IDXTYPE> >,IDXTYPE> INTERVALLER;
350   - typedef boost::shared_ptr<Data_Splitter<INTERVALLER,IDXTYPE> > PSPLITTER;
351   - typedef Data_Splitter_CV<INTERVALLER,IDXTYPE> SPLITTERCV;
352   - typedef Data_Splitter_5050<INTERVALLER,IDXTYPE> SPLITTER5050;
353   - typedef Data_Accessor_Splitting_MemMM<DATATYPE,IDXTYPE,INTERVALLER> DATAACCESSOR;
354   - typedef Distance_Euclid<DATATYPE,DIMTYPE,SUBSET> DISTANCE;
355   - typedef Classifier_kNN<RETURNTYPE,DATATYPE,IDXTYPE,DIMTYPE,SUBSET,DATAACCESSOR,DISTANCE> CLASSIFIERKNN;
356   - typedef Criterion_Wrapper<RETURNTYPE,SUBSET,CLASSIFIERKNN,DATAACCESSOR> WRAPPERKNN;
357   - typedef Sequential_Step_Straight_Threaded<RETURNTYPE,DIMTYPE,SUBSET,WRAPPERKNN,24> EVALUATOR;
358   -
359   - // Initialize dataset
360   - PSPLITTER dsp_outer(new SPLITTER5050()); // keep second half of data for independent testing of final classification performance
361   - PSPLITTER dsp_inner(new SPLITTERCV(3)); // in the course of search use the first half of data by 3-fold cross-validation in wrapper FS criterion evaluation
362   - boost::shared_ptr<Data_Scaler<DATATYPE> > dsc(new Data_Scaler_void<DATATYPE>()); // do not scale data
363   - boost::shared_ptr<std::vector<PSPLITTER> > splitters(new std::vector<PSPLITTER>); // set-up data access
364   - splitters->push_back(dsp_outer); splitters->push_back(dsp_inner);
365   - boost::shared_ptr<DATAACCESSOR> da(new DATAACCESSOR(projectedDataList, dimStatusList, splitters, dsc));
366   - da->initialize();
367   - da->setSplittingDepth(0); if(!da->getFirstSplit()) throw fst_error("50/50 data split failed.");
368   - da->setSplittingDepth(1); if(!da->getFirstSplit()) throw fst_error("3-fold cross-validation failure.");
369   - boost::shared_ptr<SUBSET> sub(new SUBSET(da->getNoOfFeatures())); // initiate the storage for subset to-be-selected
370   -
371   - { // Initialize subset from previous iteration results
372   - sub->deselect_all();
373   - int index = 0;
374   - for (int i=0; i<dimStatusList.size(); i++) {
375   - if (dimStatusList[i] == On) sub->select(index);
376   - if (dimStatusList[i] != Ignore) index++;
377   - }
378   - }
379   -
380   - // Run search
381   - boost::shared_ptr<CLASSIFIERKNN> cknn(new CLASSIFIERKNN); cknn->set_k(3); // set-up 3-Nearest Neighbor classifier based on Euclidean distances
382   - boost::shared_ptr<WRAPPERKNN> wknn(new WRAPPERKNN); // wrap the 3-NN classifier to enable its usage as FS criterion (criterion value will be estimated by 3-fold cross-val.)
383   - wknn->initialize(cknn,da);
384   - boost::shared_ptr<EVALUATOR> eval(new EVALUATOR); // set-up the standard sequential search step object (option: hybrid, ensemble, etc.)
385   - Search_DOS<RETURNTYPE,DIMTYPE,SUBSET,WRAPPERKNN,EVALUATOR> srch(eval); // set-up Sequential Forward Floating Selection search procedure
386   - srch.set_delta(1);
387   - RETURNTYPE critval_train;
388   - if(!srch.search(0,critval_train,sub,wknn,std::cout)) throw fst_error("Search not finished.");
389   -
390   - { // Update results
391   - int dslIndex = dimStatusList.size() - 1;
392   - int subIndex = da->getNoOfFeatures() - 1;
393   - for (int wlIndex = weakLearnerDimsList.size()-1; wlIndex >= 0; wlIndex--) {
394   - const int weakLearnerDims = weakLearnerDimsList[wlIndex];
395   - int numSelectedDims = 0;
396   - for (int i=0; i<weakLearnerDims; i++) {
397   - if (dimStatusList[dslIndex] != Ignore)
398   - dimStatusList[dslIndex] = sub->selected_raw(subIndex--) ? numSelectedDims++, On : Ignore;
399   - dslIndex--;
400   - }
401   -
402   - if (numSelectedDims == 0) {
403   - for (int j=0; j<weakLearnerDims; j++)
404   - dimStatusList.removeAt(dslIndex+1);
405   - weakLearnerDimsList.removeAt(wlIndex);
406   - projectedDataList.removeAt(wlIndex);
407   - weakLearners.removeAt(wlIndex);
408   - }
409   - }
410   - }
411   - }
412   - catch (fst_error &e) { qFatal("FST ERROR: %s, code=%d", e.what(), e.code()); }
413   - catch (std::exception &e) { qFatal("non-FST ERROR: %s", e.what()); }
414   - //#else // MM_SDK_TRAINABLE
415   - //qFatal("StreamwiseFS::train not supported.");
416   - //#endif // MM_SDK_TRAINABLE
417   - }
418   -
419   - dup = Dup(weakLearners);
420   -
421   - // Create map matrix
422   - int dims = 0;
423   - foreach (DimensionStatus ds, dimStatusList) if (ds == On) dims++;
424   - cv::Mat xMap(1, dims, CV_16SC1),
425   - yMap(1, dims, CV_16SC1);
426   - int index = 0;
427   - for (int i=0; i<dimStatusList.size(); i++) {
428   - if (dimStatusList[i] == On) {
429   - xMap.at<short>(0, index) = i;
430   - yMap.at<short>(0, index) = 0;
431   - index++;
432   - }
433   - }
434   -
435   - remap = Remap(xMap, yMap, cv::INTER_NEAREST);
436   - }
437   -
438   - void project(const Matrix &src, Matrix &dst) const
439   - {
440   - dst = src;
441   - dst >> dup >> mm::Cat >> remap;
442   - }
443   -
444   - void store(QDataStream &stream) const
445   - {
446   - stream << dup << remap;
447   - }
448   -
449   - void load(QDataStream &stream)
450   - {
451   - stream >> dup >> remap;
452   - }
453   -};
454   -
455   -MM_REGISTER(Feature, FST3StreamwiseFS, true)
openbr/plugins/gui.cpp
... ... @@ -5,6 +5,11 @@
5 5 #include <QMutex>
6 6 #include <QMouseEvent>
7 7 #include <QPainter>
  8 +#include <QMainWindow>
  9 +#include <QPushButton>
  10 +#include <QHBoxLayout>
  11 +#include <QFormLayout>
  12 +#include <QLineEdit>
8 13  
9 14 #include <opencv2/imgproc/imgproc.hpp>
10 15 #include "openbr_internal.h"
... ... @@ -13,6 +18,109 @@ using namespace cv;
13 18  
14 19 namespace br
15 20 {
  21 +// Generally speaking, Qt wants GUI objects to be on the main thread, and
  22 +// for the main thread to be in an event loop. We don't restrict transform
  23 +// creation to just the main thread, but in br we do compromise and put
  24 +// the main thread in an event loop (and so should any applications wanting to
  25 +// use GUI transforms). This does mean we need a way to make our QWidget subclasses
  26 +// on the main thread. We can't create them from an arbitrary thread, then move them
  27 +// (you know, since that would be crazy), so we need some tricks to get the main
  28 +// thread to make these objects.
  29 +
  30 +// Part 1. A generic interface for creating objects, the type of object
  31 +// created is not exposed in the interface.
  32 +class NominalCreation
  33 +{
  34 +public:
  35 + virtual ~NominalCreation() {}
  36 + virtual void creation()=0;
  37 +};
  38 +
  39 +// Part 2. A template class that creates an object of the specified type
  40 +// through the interface defined in part 1. The point of this is that the
  41 +// type of object created can be hidden by using a NominalCreation *.
  42 +template<typename T>
  43 +class ActualCreation : public NominalCreation
  44 +{
  45 +public:
  46 + T * basis;
  47 +
  48 + void creation()
  49 + {
  50 + basis = new T();
  51 + }
  52 +};
  53 +
  54 +// Part 3. A class that inherits from QObject, but not QWidget. This means
  55 +// we are free to move it to the main thread.
  56 +// If this object is on the main thread, and we signal one of its slots, then
  57 +// the slot will be executed by the main thread, which is what we need.
  58 +// Unfortunately, since it uses Q_OBJECT we cannot make it a template class, but
  59 +// we still want to be able to make objects of arbitrary type on the main thread,
  60 +// so that we don't need a different adaptor for every type of QWidget subclass we use.
  61 +class MainThreadCreator : public QObject
  62 +{
  63 + Q_OBJECT
  64 +public:
  65 +
  66 + MainThreadCreator()
  67 + {
  68 + this->moveToThread(QApplication::instance()->thread());
  69 + // We actually bind a signal on this object to one of its own slots.
  70 + // the signal will be emitted by a call to getItem from an abitrary
  71 + // thread.
  72 + connect(this, SIGNAL(needCreation()), this, SLOT(createThing()), Qt::BlockingQueuedConnection);
  73 + }
  74 +
  75 + // While this cannot be a template class, it can still have a template
  76 + // method. Which is useful, but the slot which will actually be executed
  77 + // by the main thread cannot be a template. So, we use the template method
  78 + // here (called from an arbitrary thread, to create an object of arbitrary type)
  79 + // to instantiate an ActualCreation object with matching type, then we hide
  80 + // the template with the NominalCreation interface, and call worker->creation
  81 + // in the slot.
  82 + template<typename T>
  83 + T * getItem()
  84 + {
  85 + // If this is called by the main thread, we can just create the object
  86 + // it's important to check, otherwise we will have problems trying to
  87 + // wait for a blocking connection that is supposed to be processed by
  88 + // the thread that is waiting.
  89 + if (QThread::currentThread() == QApplication::instance()->thread())
  90 + return new T();
  91 +
  92 + // Create the object creation interface
  93 + ActualCreation<T> * actualWorker;
  94 + actualWorker = new ActualCreation<T> ();
  95 + // hide it
  96 + worker = actualWorker;
  97 +
  98 + // emit the signal, we set up a blocking queued connection, so
  99 + // this is a blocking wait for the slot to finish being run.
  100 + emit needCreation();
  101 +
  102 + // collect the results, and return.
  103 + T * output = actualWorker->basis;
  104 + delete actualWorker;
  105 + return output;
  106 + }
  107 +
  108 + NominalCreation * worker;
  109 +
  110 +signals:
  111 + void needCreation();
  112 +
  113 +public slots:
  114 + // The actual slot, to be run by the main thread. The type
  115 + // of object being created is not, and indeed cannot, be exposed here
  116 + // since this cannot be a template method, and the class cannot be a
  117 + // template class.
  118 + void createThing()
  119 + {
  120 + worker->creation();
  121 + }
  122 +};
  123 +
16 124 QImage toQImage(const Mat &mat)
17 125 {
18 126 // Convert to 8U depth
... ... @@ -65,6 +173,7 @@ public:
65 173  
66 174 DisplayWindow(QWidget * parent = NULL) : QLabel(parent)
67 175 {
  176 + setFixedSize(200,200);
68 177 QApplication::instance()->installEventFilter(this);
69 178 }
70 179  
... ... @@ -75,7 +184,13 @@ public slots:
75 184  
76 185 show();
77 186 setPixmap(pixmap);
78   - setFixedSize(input.size());
  187 +
  188 + // We appear to get a warning on windows if we set window width < 104. This is of course not
  189 + // reflected in the Qt min size settings, and I don't know how to query it.
  190 + QSize temp = input.size();
  191 + if (temp.width() < 104)
  192 + temp.setWidth(104);
  193 + setFixedSize(temp);
79 194 }
80 195  
81 196  
... ... @@ -188,74 +303,84 @@ private:
188 303  
189 304 };
190 305  
191   -
192   -// I want a template class that doesn't look like a template class
193   -class NominalCreation
  306 +class DisplayGUI : public QMainWindow
194 307 {
195   -public:
196   - virtual ~NominalCreation() {}
197   - virtual void creation()=0;
198   -};
  308 + Q_OBJECT
199 309  
200   -// Putting the template on a subclass means we can maintain a pointer that
201   -// doesn't include T in its type.
202   -template<typename T>
203   -class ActualCreation : public NominalCreation
204   -{
205 310 public:
206   - T * basis;
207 311  
208   - void creation()
  312 + DisplayGUI(QWidget * parent = NULL) : QMainWindow(parent)
209 313 {
210   - basis = new T();
211   - }
212   -};
  314 + centralWidget = new QWidget();
  315 + layout = new QHBoxLayout();
  316 + inputLayout = new QVBoxLayout();
213 317  
214   -// We want to create a QLabel subclass on the main thread, but are running in another thread.
215   -// We cannot move QWidget subclasses to a different thread (obviously that would be crazy), but
216   -// we can create one of these, and move it to the main thread, and then use it to create the object
217   -// we want.
218   -// Additional fact: QObject subclasses cannot be template classes.
219   -class MainThreadCreator : public QObject
220   -{
221   - Q_OBJECT
222   -public:
  318 + button.setText("Set Template Metadata");
223 319  
224   - MainThreadCreator()
225   - {
226   - this->moveToThread(QApplication::instance()->thread());
  320 + layout->addWidget(&label);
227 321  
228   - connect(this, SIGNAL(needCreation()), this, SLOT(createThing()), Qt::BlockingQueuedConnection);
  322 + inputLayout->addWidget(&button);
  323 + layout->addLayout(inputLayout);
  324 +
  325 + centralWidget->setLayout(layout);
  326 +
  327 + setCentralWidget(centralWidget);
  328 +
  329 + connect(&button, SIGNAL(clicked()), this, SLOT(buttonPressed()));
229 330 }
230 331  
231   - // While this cannot be a template class, it can still have a template method.
232   - template<typename T>
233   - T * getItem()
  332 +public slots:
  333 + void showImage(const QPixmap & input)
234 334 {
235   - if (QThread::currentThread() == QApplication::instance()->thread())
236   - return new T();
  335 + pixmap = input;
  336 + foreach(const QString& label, keys) {
  337 + QLineEdit *edit = new QLineEdit;
  338 + fields.append(edit);
  339 + QFormLayout *form = new QFormLayout;
  340 + form->addRow(label, edit);
  341 + inputLayout->addLayout(form);
  342 + }
237 343  
238   - ActualCreation<T> * actualWorker;
239   - actualWorker = new ActualCreation<T> ();
240   - worker = actualWorker;
  344 + show();
  345 + label.setPixmap(pixmap);
  346 + label.setFixedSize(input.size());
  347 + }
241 348  
242   - emit needCreation();
  349 + QStringList waitForButtonPress()
  350 + {
  351 + QMutexLocker locker(&lock);
  352 + wait.wait(&lock);
243 353  
244   - T * output = actualWorker->basis;
245   - delete actualWorker;
246   - return output;
  354 + QStringList values;
  355 + for(int i = 0; i<fields.size(); i++) values.append(fields.at(i)->text());
  356 + return values;
247 357 }
248 358  
249   - NominalCreation * worker;
  359 +public slots:
250 360  
251   -signals:
252   - void needCreation();
  361 + void buttonPressed()
  362 + {
  363 + wait.wakeAll();
  364 + }
253 365  
254   -public slots:
255   - void createThing()
  366 + void setKeys(const QStringList& k)
256 367 {
257   - worker->creation();
  368 + keys = k;
258 369 }
  370 +
  371 +private:
  372 +
  373 + QWidget *centralWidget;
  374 + QStringList keys;
  375 + QList<QLineEdit*> fields;
  376 + QPushButton button;
  377 + QMutex lock;
  378 + QWaitCondition wait;
  379 + QPixmap pixmap;
  380 + QLabel label;
  381 + QHBoxLayout *layout;
  382 + QVBoxLayout *inputLayout;
  383 +
259 384 };
260 385  
261 386 /*!
... ... @@ -288,17 +413,11 @@ public:
288 413  
289 414 void train(const TemplateList &data) { (void) data; }
290 415  
291   - void project(const TemplateList &src, TemplateList &dst) const
292   - {
293   - Transform * non_const = (ShowTransform *) this;
294   - non_const->projectUpdate(src,dst);
295   - }
296   -
297 416 void projectUpdate(const TemplateList &src, TemplateList &dst)
298 417 {
299 418 dst = src;
300 419  
301   - if (src.empty() || !Globals->useGui)
  420 + if (src.empty())
302 421 return;
303 422  
304 423 foreach (const Template & t, src) {
... ... @@ -427,6 +546,98 @@ public:
427 546  
428 547 BR_REGISTER(Transform, ManualTransform)
429 548  
  549 +/*!
  550 + * \ingroup transforms
  551 + * \brief Elicits metadata for templates in a pretty GUI
  552 + * \author Scott Klum \cite sklum
  553 + */
  554 +class ElicitTransform : public TimeVaryingTransform
  555 +{
  556 + Q_PROPERTY(QStringList keys READ get_keys WRITE set_keys RESET reset_keys STORED false)
  557 + BR_PROPERTY(QStringList, keys, QStringList())
  558 +
  559 + Q_OBJECT
  560 +
  561 + MainThreadCreator creator;
  562 + DisplayGUI *gui;
  563 + QImage qImageBuffer;
  564 + QPixmap *displayBuffer;
  565 +
  566 +public:
  567 + ElicitTransform() : TimeVaryingTransform(false, false)
  568 + {
  569 + displayBuffer = NULL;
  570 + gui = NULL;
  571 + }
  572 +
  573 + ~ElicitTransform()
  574 + {
  575 + delete displayBuffer;
  576 + delete gui;
  577 + }
  578 +
  579 + void train(const TemplateList &data) { (void) data; }
  580 +
  581 + void projectUpdate(const TemplateList &src, TemplateList &dst)
  582 + {
  583 + dst = src;
  584 +
  585 + if (src.empty()) return;
  586 +
  587 + for (int i = 0; i < dst.size(); i++) {
  588 + foreach(const cv::Mat &m, dst[i]) {
  589 + qImageBuffer = toQImage(m);
  590 + displayBuffer->convertFromImage(qImageBuffer);
  591 +
  592 + emit updateImage(displayBuffer->copy(displayBuffer->rect()));
  593 +
  594 + QStringList metadata = gui->waitForButtonPress();
  595 + for(int j = 0; j < keys.size(); j++) dst[i].file.set(keys[j],metadata[j]);
  596 + }
  597 + }
  598 + }
  599 +
  600 + void finalize(TemplateList & output)
  601 + {
  602 + (void) output;
  603 + emit hideWindow();
  604 + }
  605 +
  606 + void init()
  607 + {
  608 + initActual<DisplayGUI>();
  609 + }
  610 +
  611 + template<typename GUIType>
  612 + void initActual()
  613 + {
  614 + if (!Globals->useGui)
  615 + return;
  616 +
  617 + TimeVaryingTransform::init();
  618 +
  619 + if (displayBuffer)
  620 + delete displayBuffer;
  621 +
  622 + displayBuffer = new QPixmap();
  623 +
  624 + if (gui)
  625 + delete gui;
  626 +
  627 + gui = creator.getItem<GUIType>();
  628 + gui->setKeys(keys);
  629 + // Connect our signals to the window's slots
  630 + connect(this, SIGNAL(updateImage(QPixmap)), gui,SLOT(showImage(QPixmap)));
  631 + connect(this, SIGNAL(hideWindow()), gui, SLOT(hide()));
  632 + }
  633 +
  634 +signals:
  635 +
  636 + void updateImage(const QPixmap & input);
  637 + void hideWindow();
  638 +
  639 +};
  640 +BR_REGISTER(Transform, ElicitTransform)
430 641  
431 642 /*!
432 643 * \ingroup transforms
... ... @@ -489,6 +700,11 @@ public:
489 700 BR_REGISTER(Transform, SurveyTransform)
490 701  
491 702  
  703 +/*!
  704 + * \ingroup transforms
  705 + * \brief Limits the frequency of projects going through this transform to the input targetFPS
  706 + * \author Charles Otto \cite caotto
  707 + */
492 708 class FPSLimit : public TimeVaryingTransform
493 709 {
494 710 Q_OBJECT
... ... @@ -539,6 +755,12 @@ protected:
539 755 };
540 756 BR_REGISTER(Transform, FPSLimit)
541 757  
  758 +/*!
  759 + * \ingroup transforms
  760 + * \brief Calculates the average FPS of projects going through this transform, stores the result in AvgFPS
  761 + * Reports an average FPS from the initialization of this transform onwards.
  762 + * \author Charles Otto \cite caotto
  763 + */
542 764 class FPSCalc : public TimeVaryingTransform
543 765 {
544 766 Q_OBJECT
... ...
openbr/plugins/keypoint.cpp
... ... @@ -222,9 +222,9 @@ class GridTransform : public UntrainableTransform
222 222 QList<QPointF> landmarks;
223 223 const float row_step = 1.f * src.m().rows / rows;
224 224 const float column_step = 1.f * src.m().cols / columns;
225   - for (float i=row_step/2; i<src.m().rows; i+=row_step)
226   - for (float j=column_step/2; j<src.m().cols; j+=column_step)
227   - landmarks.append(QPointF(i,j));
  225 + for (float y=row_step/2; y<src.m().rows; y+=row_step)
  226 + for (float x=column_step/2; x<src.m().cols; x+=column_step)
  227 + landmarks.append(QPointF(x,y));
228 228 dst = src;
229 229 dst.file.setPoints(landmarks);
230 230 }
... ...
openbr/plugins/misc.cpp
... ... @@ -332,57 +332,6 @@ class AnonymizeTransform : public UntrainableMetaTransform
332 332  
333 333 BR_REGISTER(Transform, AnonymizeTransform)
334 334  
335   -// TODO: Use a global Mutex to prevent concurrent reads from stdin
336   -#if 0
337   -/*!
338   - * \ingroup transforms
339   - * \brief Name a point
340   - * \author Scott Klum \cite sklum
341   - */
342   -class ElicitMetadataTransform : public UntrainableMetaTransform
343   -{
344   - Q_OBJECT
345   -
346   - Q_PROPERTY(QStringList metadata READ get_metadata WRITE set_metadata RESET reset_metadata STORED false)
347   - BR_PROPERTY(QStringList, metadata, QStringList())
348   -
349   - void init()
350   - {
351   - Globals->setProperty("parallelism", "0"); // Can only work in single threaded mode
352   - }
353   -
354   - void project(const Template &src, Template &dst) const
355   - {
356   - dst = src;
357   -
358   - QTextStream stream(stdin);
359   -
360   - foreach (const QString &key, metadata) {
361   - qDebug() << "Specify a value for key: " << key;
362   - QString value = stream.readLine();
363   - if (value[0] == '(') {
364   - QStringList values = value.split(',');
365   - if (values.size() == 2) /* QPointF */ {
366   - values[1].chop(1);
367   - QPointF point(values[0].mid(1).toFloat(), values[1].toFloat());
368   - if (key != "Points") dst.file.set(key, point);
369   - else dst.file.appendPoint(point);
370   - }
371   - else /* QRectF */ {
372   - values[3].chop(1);
373   - QRectF rect(values[0].mid(1).toFloat(), values[1].toFloat(), values[2].toFloat(), values[3].toFloat());
374   - if (key != "Rects") dst.file.set(key, rect);
375   - else dst.file.appendRect(rect);
376   - }
377   - }
378   - else dst.file.set(key, value);
379   - }
380   - }
381   -};
382   -
383   -BR_REGISTER(Transform, ElicitMetadataTransform)
384   -#endif
385   -
386 335 /*!
387 336 * \ingroup transforms
388 337 * \brief Change the br::Template::file extension
... ... @@ -405,27 +354,31 @@ BR_REGISTER(Transform, AsTransform)
405 354  
406 355 /*!
407 356 * \ingroup transforms
408   - * \brief Change the template subject using a regular expresion matched to the file's base name.
409   - * \author Josh Klontz \cite jklontz
  357 + * \brief Apply the input regular expression to the value of inputProperty, store the matched portion in outputProperty.
  358 + * \author Charles Otto \cite caotto
410 359 */
411   -class SubjectTransform : public UntrainableMetaTransform
  360 +class RegexPropertyTransform : public UntrainableMetaTransform
412 361 {
413 362 Q_OBJECT
414 363 Q_PROPERTY(QString regexp READ get_regexp WRITE set_regexp RESET reset_regexp STORED false)
  364 + Q_PROPERTY(QString inputProperty READ get_inputProperty WRITE set_inputProperty RESET reset_inputProperty STORED false)
  365 + Q_PROPERTY(QString outputProperty READ get_outputProperty WRITE set_outputProperty RESET reset_outputProperty STORED false)
415 366 BR_PROPERTY(QString, regexp, "(.*)")
  367 + BR_PROPERTY(QString, inputProperty, "name")
  368 + BR_PROPERTY(QString, outputProperty, "Label")
416 369  
417 370 void project(const Template &src, Template &dst) const
418 371 {
419 372 dst = src;
420 373 QRegularExpression re(regexp);
421   - QRegularExpressionMatch match = re.match(dst.file.baseName());
  374 + QRegularExpressionMatch match = re.match(dst.file.get<QString>(inputProperty));
422 375 if (!match.hasMatch())
423   - qFatal("Unable to match regular expression \"%s\" to base name \"%s\"!", qPrintable(regexp), qPrintable(dst.file.baseName()));
424   - dst.file.set("Subject", match.captured(match.lastCapturedIndex()));
  376 + qFatal("Unable to match regular expression \"%s\" to base name \"%s\"!", qPrintable(regexp), qPrintable(dst.file.get<QString>(inputProperty)));
  377 + dst.file.set(outputProperty, match.captured(match.lastCapturedIndex()));
425 378 }
426 379 };
427 380  
428   -BR_REGISTER(Transform, SubjectTransform)
  381 +BR_REGISTER(Transform, RegexPropertyTransform)
429 382  
430 383 /*!
431 384 * \ingroup transforms
... ... @@ -521,6 +474,73 @@ class RestoreMatTransform : public UntrainableMetaTransform
521 474 BR_REGISTER(Transform, RestoreMatTransform)
522 475  
523 476  
  477 +/*!
  478 + * \ingroup transforms
  479 + * \brief Incrementally output templates received to a gallery, based on the current filename
  480 + * When a template is received in projectUpdate for the first time since a finalize, open a new gallery based on the
  481 + * template's filename, and the galleryFormat property.
  482 + * Templates received in projectUpdate will be output to the gallery with a filename combining their original filename and
  483 + * their FrameNumber property, with the file extension specified by the fileFormat property.
  484 + * \author Charles Otto \cite caotto
  485 + */
  486 +class IncrementalOutputTransform : public TimeVaryingTransform
  487 +{
  488 + Q_OBJECT
  489 +
  490 + Q_PROPERTY(QString galleryFormat READ get_galleryFormat WRITE set_galleryFormat RESET reset_galleryFormat STORED false)
  491 + Q_PROPERTY(QString fileFormat READ get_fileFormat WRITE set_fileFormat RESET reset_fileFormat STORED false)
  492 + BR_PROPERTY(QString, galleryFormat, "")
  493 + BR_PROPERTY(QString, fileFormat, ".png")
  494 +
  495 + bool galleryUp;
  496 +
  497 + void projectUpdate(const TemplateList &src, TemplateList &dst)
  498 + {
  499 + if (src.empty())
  500 + return;
  501 +
  502 + if (!galleryUp) {
  503 + QFileInfo finfo(src[0].file.name);
  504 + QString galleryName = finfo.baseName() + galleryFormat;
  505 +
  506 + writer = QSharedPointer<Gallery> (Factory<Gallery>::make(galleryName));
  507 + galleryUp = true;
  508 + }
  509 +
  510 + dst = src;
  511 + foreach(const Template & t, src) {
  512 + if (t.empty())
  513 + continue;
  514 +
  515 + // Build the output filename for this template
  516 + QFileInfo finfo(t.file.name);
  517 + QString outputName = finfo.baseName() +"_" + t.file.get<QString>("FrameNumber") + fileFormat;
  518 +
  519 + Template out = t;
  520 + out.file.name = outputName;
  521 + writer->write(out);
  522 + }
  523 + }
  524 +
  525 + void train(const TemplateList& data)
  526 + {
  527 + (void) data;
  528 + }
  529 +
  530 + // Drop the current gallery.
  531 + void finalize(TemplateList & data)
  532 + {
  533 + (void) data;
  534 + galleryUp = false;
  535 + }
  536 +
  537 + QSharedPointer<Gallery> writer;
  538 +public:
  539 + IncrementalOutputTransform() : TimeVaryingTransform(false,false) {galleryUp = false;}
  540 +};
  541 +
  542 +BR_REGISTER(Transform, IncrementalOutputTransform)
  543 +
524 544 class EventTransform : public UntrainableMetaTransform
525 545 {
526 546 Q_OBJECT
... ...
openbr/plugins/openbr_internal.h
... ... @@ -116,12 +116,12 @@ public:
116 116  
117 117 virtual void project(const Template &src, Template &dst) const
118 118 {
119   - timeInvariantAlias->project(src,dst);
  119 + timeInvariantAlias.project(src,dst);
120 120 }
121 121  
122 122 virtual void project(const TemplateList &src, TemplateList &dst) const
123 123 {
124   - timeInvariantAlias->project(src,dst);
  124 + timeInvariantAlias.project(src,dst);
125 125 }
126 126  
127 127 // Get a compile failure if this isn't here to go along with the other
... ... @@ -150,21 +150,13 @@ public:
150 150 return this->clone();
151 151 }
152 152  
153   - void init()
154   - {
155   - delete timeInvariantAlias;
156   - timeInvariantAlias = new TimeInvariantWrapperTransform(this);
157   - }
158   -
159 153 protected:
160   - Transform * timeInvariantAlias;
161   - TimeVaryingTransform(bool independent = true, bool trainable = true) : Transform(independent, trainable)
  154 + // Since copies aren't actually made until project is called, we can set up
  155 + // timeInvariantAlias in the constructor.
  156 + TimeInvariantWrapperTransform timeInvariantAlias;
  157 + TimeVaryingTransform(bool independent = true, bool trainable = true) : Transform(independent, trainable), timeInvariantAlias(this)
162 158 {
163   - timeInvariantAlias = NULL;
164   - }
165   - ~TimeVaryingTransform()
166   - {
167   - delete timeInvariantAlias;
  159 + //
168 160 }
169 161 };
170 162  
... ... @@ -183,7 +175,7 @@ public:
183 175 virtual void project(const Template &src, Template &dst) const
184 176 {
185 177 if (timeVarying()) {
186   - timeInvariantAlias->project(src,dst);
  178 + timeInvariantAlias.project(src,dst);
187 179 return;
188 180 }
189 181 _project(src, dst);
... ... @@ -192,7 +184,7 @@ public:
192 184 virtual void project(const TemplateList &src, TemplateList &dst) const
193 185 {
194 186 if (timeVarying()) {
195   - timeInvariantAlias->project(src,dst);
  187 + timeInvariantAlias.project(src,dst);
196 188 return;
197 189 }
198 190 _project(src, dst);
... ... @@ -209,10 +201,6 @@ public:
209 201 isTimeVarying = isTimeVarying || transform->timeVarying();
210 202 trainable = trainable || transform->trainable;
211 203 }
212   -
213   - // If we are time varying, set up timeInvariantAlias
214   - if (this->timeVarying())
215   - TimeVaryingTransform::init();
216 204 }
217 205  
218 206 /*!
... ...
openbr/plugins/phash.cmake deleted
1   -set(BR_WITH_PHASH OFF CACHE BOOL "Build with pHash")
2   -
3   -if(${BR_WITH_PHASH})
4   - find_package(pHash REQUIRED)
5   - find_package(CImg REQUIRED)
6   - set(BR_THIRDPARTY_SRC ${BR_THIRDPARTY_SRC} plugins/phash.cpp ${PHASH_SRC})
7   -endif()
openbr/plugins/phash.cpp deleted
1   -#include <opencv2/core/core.hpp>
2   -#include <pHash.h>
3   -#include <mm_plugin.h>
4   -
5   -using namespace mm;
6   -
7   -/**** PHASH ****/
8   -class pHashEnroll : public UntrainableFeature
9   -{
10   - void project(const Matrix &src, Matrix &dst) const
11   - {
12   - CImg<uint8_t> cImg(src.data, src.cols, src.rows, 1, src.channels());
13   - cv::Mat m(1, sizeof(ulong64), CV_8UC1);
14   - ulong64 hash;
15   - if (ph_dct_imagehash(cImg, hash) == -1)
16   - qFatal("pHashEnroll::project ph_dct_imagehash failure for file %s.", qPrintable(src.metadata.fileName));
17   - memcpy(m.data, &hash, sizeof(ulong64));
18   - dst = Matrix(m, src.metadata);
19   - }
20   -
21   - /*** Taken from pHash, modified to take in a CImg instead of a file. ***/
22   - static CImg<float>* ph_dct_matrix(const int N){
23   - CImg<float> *ptr_matrix = new CImg<float>(N,N,1,1,1/sqrt((float)N));
24   - const float c1 = sqrt(2.0/N);
25   - for (int x=0;x<N;x++){
26   - for (int y=1;y<N;y++){
27   - *ptr_matrix->data(x,y) = c1*cos((cimg::PI/2/N)*y*(2*x+1));
28   - }
29   - }
30   - return ptr_matrix;
31   - }
32   -
33   - static int ph_dct_imagehash(CImg<uint8_t> src, ulong64 &hash)
34   - {
35   - CImg<float> meanfilter(7,7,1,1,1);
36   - CImg<float> img;
37   - if (src.spectrum() == 3){
38   - img = src.RGBtoYCbCr().channel(0).get_convolve(meanfilter);
39   - } else if (src.spectrum() == 4){
40   - int width = img.width();
41   - int height = img.height();
42   - int depth = img.depth();
43   - img = src.crop(0,0,0,0,width-1,height-1,depth-1,2).RGBtoYCbCr().channel(0).get_convolve(meanfilter);
44   - } else {
45   - img = src.channel(0).get_convolve(meanfilter);
46   - }
47   -
48   - img.resize(32,32);
49   - CImg<float> *C = ph_dct_matrix(32);
50   - CImg<float> Ctransp = C->get_transpose();
51   -
52   - CImg<float> dctImage = (*C)*img*Ctransp;
53   -
54   - CImg<float> subsec = dctImage.crop(1,1,8,8).unroll('x');;
55   -
56   - float median = subsec.median();
57   - ulong64 one = 0x0000000000000001;
58   - hash = 0x0000000000000000;
59   - for (int i=0;i< 64;i++){
60   - float current = subsec(i);
61   - if (current > median)
62   - hash |= one;
63   - one = one << 1;
64   - }
65   -
66   - delete C;
67   -
68   - return 0;
69   - }
70   -};
71   -
72   -MM_REGISTER(Feature, pHashEnroll, false)
73   -
74   -
75   -/**** PHASH_COMPARE ****/
76   -class pHashCompare : public ComparerBase
77   -{
78   - float compare(const cv::Mat &a, const cv::Mat &b) const
79   - {
80   - return 1.f - 1.f * ph_hamming_distance(*reinterpret_cast<ulong64*>(a.data), *reinterpret_cast<ulong64*>(b.data)) / 64;
81   - }
82   -};
83   -
84   -MM_REGISTER(Comparer, pHashCompare, false)
85   -
86   -
87   -/**** PHASH ****/
88   -class pHash : public Algorithm
89   -{
90   - QString algorithm() const
91   - {
92   - return "Open+pHashEnroll:Identity:pHashCompare";
93   - }
94   -};
95   -
96   -MM_REGISTER(Algorithm, pHash, false)
openbr/plugins/pp4.cmake deleted
1   -set(BR_WITH_PP4 OFF CACHE BOOL "Build with PittPatt 4")
2   -
3   -if(${BR_WITH_PP4})
4   - find_package(PP4 REQUIRED)
5   - set(BR_THIRDPARTY_SRC ${BR_THIRDPARTY_SRC} plugins/pp4.cpp)
6   - set(BR_THIRDPARTY_LIBS ${BR_THIRDPARTY_LIBS} ${PP4_LIBS})
7   - install(DIRECTORY ${PP4_DIR}/lib/ DESTINATION lib)
8   - install(DIRECTORY ${PP4_DIR}/models/ DESTINATION models/pp4)
9   -endif()
openbr/plugins/pp4.cpp deleted
1   -#include <QThreadPool>
2   -#include <pittpatt_errors.h>
3   -#include <pittpatt_nc_sdk.h>
4   -#include <pittpatt_raw_image_io.h>
5   -#include <pittpatt_license.h>
6   -#include <mm_plugin.h>
7   -
8   -#define TRY(CC) \
9   -{ \
10   - if ((CC) != PPR_SUCCESS) qFatal("%d error (%s, %d): %s.", CC, __FILE__, __LINE__, ppr_error_message(CC)); \
11   -}
12   -
13   -#define TRY_VIDEO(CC) \
14   -{ \
15   - if ((CC) != PPR_VIDEO_IO_SUCCESS) qFatal("%d error (%s, %d): %s.", CC, __FILE__, __LINE__, ppr_video_io_error_message(CC)); \
16   -}
17   -
18   -#define TRY_RAW_IMAGE(CC) \
19   -{ \
20   - if ((CC) != PPR_RAW_IMAGE_SUCCESS) qFatal("%d error (%s, %d): %s.", CC, __FILE__, __LINE__, ppr_raw_image_error_message(CC)); \
21   -}
22   -
23   -using namespace mm;
24   -
25   -/*!
26   - * \brief PittPatt 4 context
27   - * \author Josh Klontz \cite jklontz
28   - * \warning Needs a maintainer.
29   - */
30   -struct PP4Context
31   -{
32   - static ppr_context_type context;
33   -
34   - static void createRawImage(const cv::Mat &src, ppr_raw_image_type &dst)
35   - {
36   - ppr_raw_image_create(&dst, src.cols, src.rows, PPR_RAW_IMAGE_BGR24);
37   - assert((src.type() == CV_8UC3) && src.isContinuous());
38   - memcpy(dst.data, src.data, 3*src.rows*src.cols);
39   - }
40   -
41   - static void createMat(const ppr_template_type &src, cv::Mat &dst)
42   - {
43   - ppr_flat_template_type flat_template;
44   - TRY(ppr_flatten_template(context,src,&flat_template))
45   - dst = cv::Mat(1, flat_template.num_bytes, CV_8UC1, flat_template.data).clone();
46   - ppr_free_flat_template(flat_template);
47   - }
48   -
49   - static void createTemplate(const cv::Mat &src, ppr_template_type *dst)
50   - {
51   - ppr_flat_template_type flat_template;
52   - flat_template.num_bytes = src.cols;
53   - flat_template.data = src.data;
54   - TRY(ppr_unflatten_template(context, flat_template, dst))
55   - }
56   -
57   - static QString toString(const ppr_landmark_category_type &category)
58   - {
59   - switch (category) {
60   - case PPR_LANDMARK_LEFT_EYE:
61   - return "Left_Eye";
62   - case PPR_LANDMARK_RIGHT_EYE:
63   - return "Right_Eye";
64   - case PPR_LANDMARK_NOSE_BASE:
65   - return "Nose_Base";
66   - case PPR_LANDMARK_NOSE_BRIDGE:
67   - return "Nose_Bridge";
68   - case PPR_LANDMARK_NOSE_TIP:
69   - return "Nose_Tip";
70   - case PPR_LANDMARK_NOSE_TOP:
71   - return "Nose_Top";
72   - case PPR_LANDMARK_EYE_NOSE:
73   - return "Eye_Nose";
74   - case PPR_LANDMARK_MOUTH:
75   - return "Mouth";
76   - }
77   -
78   - return "Unknown";
79   - }
80   -
81   - static File toMetadata(const ppr_object_type &object)
82   - {
83   - File metadata;
84   - metadata.insert("PP4_Object_X", object.position.x - object.dimensions.width/2);
85   - metadata.insert("PP4_Object_Y", object.position.y - object.dimensions.height/2);
86   - metadata.insert("PP4_Object_Width", object.dimensions.width);
87   - metadata.insert("PP4_Object_Height", object.dimensions.height);
88   - metadata.insert("PP4_Object_Confidence", object.confidence);
89   - metadata.insert("PP4_Object_Roll", object.rotation.roll);
90   - metadata.insert("PP4_Object_Pitch", object.rotation.pitch);
91   - metadata.insert("PP4_Object_Yaw", object.rotation.yaw);
92   - metadata.insert("PP4_Object_Precision", object.rotation.precision);
93   - metadata.insert("PP4_Object_ModelID", object.model_id);
94   - metadata.insert("PP4_Object_NumLandmarks", object.num_landmarks);
95   - metadata.insert("PP4_Object_Size", object.size);
96   -
97   - QList<ppr_landmark_category_type> categories;
98   - categories << PPR_LANDMARK_RIGHT_EYE
99   - << PPR_LANDMARK_LEFT_EYE
100   - << PPR_LANDMARK_NOSE_BASE
101   - << PPR_LANDMARK_NOSE_BRIDGE
102   - << PPR_LANDMARK_NOSE_TIP
103   - << PPR_LANDMARK_NOSE_TOP
104   - << PPR_LANDMARK_EYE_NOSE
105   - << PPR_LANDMARK_MOUTH;
106   -
107   - for (int i=0; i<categories.size(); i++) {
108   - ppr_landmark_category_type category = categories[i];
109   - QString metadataString = QString("PP4_Landmark%1_%2").arg(QString::number(i), toString(category));
110   -
111   - bool found = false;
112   - for (int j=0; j<object.num_landmarks; j++) {
113   - ppr_landmark_type &landmark = object.landmarks[j];
114   - if (landmark.category != category) continue;
115   -
116   - metadata.insert(metadataString+"_X", landmark.position.x);
117   - metadata.insert(metadataString+"_Y", landmark.position.y);
118   - metadata.insert(metadataString+"_Category", landmark.category);
119   - metadata.insert(metadataString+"_ModelID", landmark.model_id);
120   - metadata.insert(metadataString+"_Index", j);
121   - found = true;
122   - break;
123   - }
124   -
125   - if (!found) {
126   - metadata.insert(metadataString+"_X", -1);
127   - metadata.insert(metadataString+"_Y", -1);
128   - metadata.insert(metadataString+"_Category", -1);
129   - metadata.insert(metadataString+"_ModelID", -1);
130   - metadata.insert(metadataString+"_Index", -1);
131   - }
132   - }
133   -
134   - return metadata;
135   - }
136   -
137   - static ppr_object_type fromMetadata(const File &metadata)
138   - {
139   - ppr_object_type object;
140   -
141   - object.position.x = metadata.value("PP4_Object_X").toFloat() + metadata.value("PP4_Object_Width").toFloat()/2;
142   - object.position.y = metadata.value("PP4_Object_Y").toFloat() + metadata.value("PP4_Object_Height").toFloat()/2;
143   - object.dimensions.width = metadata.value("PP4_Object_Width").toFloat();
144   - object.dimensions.height = metadata.value("PP4_Object_Height").toFloat();
145   - object.confidence = metadata.value("PP4_Object_Confidence").toFloat();
146   - object.rotation.roll = metadata.value("PP4_Object_Roll").toFloat();
147   - object.rotation.pitch = metadata.value("PP4_Object_Pitch").toFloat();
148   - object.rotation.yaw = metadata.value("PP4_Object_Yaw").toFloat();
149   - object.rotation.precision = (ppr_precision_type) metadata.value("PP4_Object_Precision").toFloat();
150   - object.model_id = metadata.value("PP4_Object_ModelID").toInt();
151   - object.num_landmarks = metadata.value("PP4_Object_NumLandmarks").toInt();
152   - object.size = metadata.value("PP4_Object_Size").toFloat();
153   -
154   - QStringList landmarkNames = QStringList(metadata.keys()).filter(QRegExp("(.*)_Category")).replaceInStrings("_Category", "");
155   - object.landmarks = new ppr_landmark_type[object.num_landmarks];
156   - for (int j=0; j<landmarkNames.size(); j++) {
157   - int landmarkIndex = metadata.value(landmarkNames[j]+"_Index").toInt();
158   - if (landmarkIndex == -1) continue;
159   - object.landmarks[landmarkIndex].position.x = metadata.value(landmarkNames[j]+"_X").toFloat();
160   - object.landmarks[landmarkIndex].position.y = metadata.value(landmarkNames[j]+"_Y").toFloat();
161   - object.landmarks[landmarkIndex].category = (ppr_landmark_category_type)metadata.value(landmarkNames[j]+"_Category").toInt();
162   - object.landmarks[landmarkIndex].model_id = metadata.value(landmarkNames[j]+"_ModelID").toInt();
163   - landmarkIndex++;
164   - }
165   -
166   - return object;
167   - }
168   -
169   - static void freeObject(ppr_object_type &object)
170   - {
171   - delete[] object.landmarks;
172   - object.landmarks = NULL;
173   - object.num_landmarks = 0;
174   - }
175   -};
176   -
177   -ppr_context_type PP4Context::context;
178   -
179   -/*!
180   - * \ingroup initializers
181   - * \brief Initialize PittPatt 4
182   - * \author Josh Klontz \cite jklontz
183   - * \warning Needs a maintainer.
184   - */
185   -class PP4Initializer : public Initializer
186   - , public PP4Context
187   -{
188   - Q_OBJECT
189   -
190   - void initialize() const
191   - {
192   - context = ppr_get_context();
193   - TRY(ppr_enable_recognition(context))
194   - TRY(ppr_set_license(context, my_license_id, my_license_key))
195   - TRY(ppr_set_models_path(context, qPrintable(Globals->SDKPath + "/models/pp4")))
196   - TRY(ppr_set_num_recognition_threads(context, QThreadPool::globalInstance()->maxThreadCount()))
197   - TRY(ppr_set_num_detection_threads(context, 1))
198   - TRY(ppr_set_detection_precision(context, PPR_FINE_PRECISION))
199   - TRY(ppr_set_landmark_detector_type(context, PPR_DUAL_MULTI_POSE_LANDMARK_DETECTOR, PPR_AUTOMATIC_LANDMARKS))
200   - TRY(ppr_set_min_size(context, 4))
201   - TRY(ppr_set_frontal_yaw_constraint(context, PPR_FRONTAL_YAW_CONSTRAINT_PERMISSIVE))
202   - TRY(ppr_set_template_extraction_type(context, PPR_EXTRACT_DOUBLE))
203   - TRY(ppr_initialize_context(context))
204   - Globals->Abbreviations.insert("PP4", "Open+PP4Detect!PP4Enroll:PP4Compare");
205   - }
206   -
207   - void finalize() const
208   - {
209   - TRY(ppr_release_context(context))
210   - ppr_finalize_sdk();
211   - }
212   -};
213   -
214   -MM_REGISTER(Initializer, PP4Initializer, "")
215   -
216   -/*!
217   - * \ingroup transforms
218   - * \brief Detect a face in PittPatt 4
219   - * \author Josh Klontz \cite jklontz
220   - * \warning Needs a maintainer.
221   - */
222   -class PP4Detect : public UntrainableMetaFeature
223   - , public PP4Context
224   -{
225   - Q_OBJECT
226   -
227   - void project(const Template &src, Template &dst) const
228   - {
229   - dst.file = src.file;
230   -
231   - foreach (const cv::Mat &matrix, src) {
232   - ppr_raw_image_type raw_image;
233   - createRawImage(matrix, raw_image);
234   - ppr_image_type image;
235   - TRY(ppr_create_image(raw_image, &image))
236   - ppr_object_list_type object_list;
237   - TRY(ppr_detect_objects(context, image, &object_list))
238   -
239   - QList<ppr_object_type> objects;
240   - if (src.file.getBool("ForceEnrollment")) objects = getBestObject(object_list);
241   - else objects = getAllObjects(object_list);
242   -
243   - foreach (const ppr_object_type &object, objects) {
244   - dst.file.append(toMetadata(object));
245   - dst += matrix;
246   - }
247   -
248   - ppr_free_object_list(object_list);
249   - ppr_free_image(image);
250   - ppr_raw_image_free(raw_image);
251   - }
252   -
253   - if (src.file.getBool("ForceEnrollment") && dst.isEmpty()) dst += cv::Mat();
254   - }
255   -
256   -private:
257   - QList<ppr_object_type> getBestObject(ppr_object_list_type object_list) const
258   - {
259   - int best_index = -1;
260   - float best_confidence = 0;
261   - for (int i=0; i<object_list.num_objects; i++) {
262   - ppr_object_type object = object_list.objects[i];
263   - ppr_object_suitability_type suitability;
264   - TRY(ppr_is_object_suitable_for_recognition(context, object, &suitability))
265   - if (suitability != PPR_OBJECT_SUITABLE_FOR_RECOGNITION) continue;
266   - if ((object.confidence > best_confidence) ||
267   - (best_index == -1)) {
268   - best_confidence = object.confidence;
269   - best_index = i;
270   - }
271   - }
272   -
273   - QList<ppr_object_type> objects;
274   - if (best_index != -1) objects.append(object_list.objects[best_index]);
275   - return objects;
276   - }
277   -
278   - QList<ppr_object_type> getAllObjects(ppr_object_list_type object_list) const
279   - {
280   - QList<ppr_object_type> objects;
281   - for (int i=0; i<object_list.num_objects; i++)
282   - objects.append(object_list.objects[i]);
283   - return objects;
284   - }
285   -};
286   -
287   -MM_REGISTER(Feature, PP4Detect, "")
288   -
289   -/*!
290   - * \ingroup transforms
291   - * \brief Enroll face in PittPatt 4
292   - * \author Josh Klontz \cite jklontz
293   - * \warning Needs a maintainer.
294   - */
295   -class PP4Enroll : public UntrainableMetaFeature
296   - , public PP4Context
297   -{
298   - Q_OBJECT
299   -
300   - void project(const Template &src, Template &dst) const
301   - {
302   - if (!src.m().data) {
303   - dst += cv::Mat();
304   - return;
305   - }
306   -
307   - ppr_raw_image_type raw_image;
308   - createRawImage(src, raw_image);
309   - ppr_image_type image;
310   - TRY(ppr_create_image(raw_image, &image))
311   -
312   - ppr_object_type object = fromMetadata(src.file);
313   -
314   - ppr_template_type curr_template;
315   - TRY(ppr_extract_template_from_object(context, image, object, &curr_template))
316   -
317   - freeObject(object);
318   -
319   - cv::Mat m;
320   - createMat(curr_template, m);
321   - dst += m;
322   -
323   - ppr_free_template(curr_template);
324   - ppr_free_image(image);
325   - ppr_raw_image_free(raw_image);
326   - }
327   -};
328   -
329   -MM_REGISTER(Feature, PP4Enroll, "")
330   -
331   -
332   -class PP4Compare : public Comparer,
333   - public PP4Context
334   -{
335   - Q_OBJECT
336   -
337   - void compare(const TemplateList &target, const TemplateList &query, Output *output) const
338   - {
339   - ppr_gallery_type target_gallery, query_gallery;
340   - ppr_create_gallery(context, &target_gallery);
341   - ppr_create_gallery(context, &query_gallery);
342   - QList<int> target_template_ids, query_template_ids;
343   - enroll(target, &target_gallery, target_template_ids);
344   - enroll(query, &query_gallery, query_template_ids);
345   -
346   - ppr_similarity_matrix_type similarity_matrix;
347   - TRY(ppr_compare_galleries(context, query_gallery, target_gallery, &similarity_matrix))
348   -
349   - for (int i=0; i<query_template_ids.size(); i++) {
350   - int query_template_id = query_template_ids[i];
351   - for (int j=0; j<target_template_ids.size(); j++) {
352   - int target_template_id = target_template_ids[j];
353   - float score = -std::numeric_limits<float>::max();
354   - if ((query_template_id != -1) && (target_template_id != -1)) {
355   - TRY(ppr_get_similarity_matrix_element(context, similarity_matrix, query_template_id, target_template_id, &score))
356   - }
357   - output->setData(score, i, j);
358   - }
359   - }
360   -
361   - ppr_free_similarity_matrix(similarity_matrix);
362   - ppr_free_gallery(target_gallery);
363   - ppr_free_gallery(query_gallery);
364   - }
365   -
366   - void enroll(const TemplateList &templates, ppr_gallery_type *gallery, QList<int> &template_ids) const
367   - {
368   - foreach (const Template &t, templates) {
369   - if (t.m().data) {
370   - ppr_template_type u;
371   - createTemplate(t.m(), &u);
372   - int template_id;
373   - TRY(ppr_copy_template_to_gallery(context, gallery, u, &template_id))
374   - template_ids.append(template_id);
375   - ppr_free_template(u);
376   - } else {
377   - template_ids.append(-1);
378   - }
379   - }
380   - }
381   -};
382   -
383   -MM_REGISTER(Comparer, PP4Compare, "")
384   -
385   -#include "plugins/pp4.moc"
openbr/plugins/stasm4.cpp
... ... @@ -54,6 +54,9 @@ class StasmTransform : public UntrainableTransform
54 54 {
55 55 Q_OBJECT
56 56  
  57 + Q_PROPERTY(bool stasm3Format READ get_stasm3Format WRITE set_stasm3Format RESET reset_stasm3Format STORED false)
  58 + BR_PROPERTY(bool, stasm3Format, false)
  59 +
57 60 Resource<StasmCascadeClassifier> stasmCascadeResource;
58 61  
59 62 void init()
... ... @@ -69,14 +72,20 @@ class StasmTransform : public UntrainableTransform
69 72 StasmCascadeClassifier *stasmCascade = stasmCascadeResource.acquire();
70 73  
71 74 int foundface;
  75 + int nLandmarks = stasm_NLANDMARKS;
72 76 float landmarks[2 * stasm_NLANDMARKS];
73 77 stasm_search_single(&foundface, landmarks, reinterpret_cast<const char*>(src.m().data), src.m().cols, src.m().rows, *stasmCascade, NULL, NULL);
74 78  
  79 + if (stasm3Format) {
  80 + nLandmarks = 76;
  81 + stasm_convert_shape(landmarks, nLandmarks);
  82 + }
  83 +
75 84 stasmCascadeResource.release(stasmCascade);
76 85  
77 86 if (!foundface) qWarning("No face found in %s", qPrintable(src.file.fileName()));
78 87 else {
79   - for (int i = 0; i < stasm_NLANDMARKS; i++)
  88 + for (int i = 0; i < nLandmarks; i++)
80 89 dst.file.appendPoint(QPointF(landmarks[2 * i], landmarks[2 * i + 1]));
81 90 }
82 91  
... ...
openbr/plugins/stream.cpp
... ... @@ -364,6 +364,7 @@ protected:
364 364 QMutex last_frame_update;
365 365 };
366 366  
  367 +static QMutex openLock;
367 368 // Read a video frame by frame using cv::VideoCapture
368 369 class VideoDataSource : public DataSource
369 370 {
... ... @@ -396,6 +397,8 @@ public:
396 397 // Yes, we should specify absolute path:
397 398 // http://stackoverflow.com/questions/9396459/loading-a-video-in-opencv-in-python
398 399 QString fileName = (Globals->path.isEmpty() ? "" : Globals->path + "/") + input.file.name;
  400 + // On windows, this appears to not be thread-safe
  401 + QMutexLocker lock(&openLock);
399 402 video.open(QFileInfo(fileName).absoluteFilePath().toStdString());
400 403 }
401 404  
... ... @@ -1119,7 +1122,10 @@ public:
1119 1122 dst = src;
1120 1123  
1121 1124 bool res = readStage->dataSource.open(dst);
1122   - if (!res) return;
  1125 + if (!res) {
  1126 + qDebug("stream failed to open %s", qPrintable(dst[0].file.name));
  1127 + return;
  1128 + }
1123 1129  
1124 1130 // Start the first thread in the stream.
1125 1131 QWriteLocker lock(&readStage->statusLock);
... ... @@ -1368,11 +1374,6 @@ public:
1368 1374 if (!transform)
1369 1375 return;
1370 1376  
1371   - // Set up timeInvariantAlias
1372   - // this is only safe because copies are actually made in project
1373   - // calls, not during init.
1374   - TimeVaryingTransform::init();
1375   -
1376 1377 trainable = transform->trainable;
1377 1378  
1378 1379 basis.setParent(this->parent());
... ...
openbr/plugins/topsurf.cmake deleted
1   -set(BR_WITH_TOPSURF OFF CACHE BOOL "Build with TOP-SURF")
2   -
3   -if(${BR_WITH_TOPSURF})
4   - find_package(TopSurf REQUIRED)
5   - set(THIRDPARTY_SRC ${THIRDPARTY_SRC} plugins/topsurf.cpp ${TOPSURF_SRC} ${TOPSURF_FLANN_SRC})
6   - install(DIRECTORY ${TOPSURF_DIR}/dictionary_10000
7   - ${TOPSURF_DIR}/dictionary_20000
8   - ${TOPSURF_DIR}/dictionary_40000
9   - DESTINATION models/topsurf)
10   -endif()
openbr/plugins/topsurf.cpp deleted
1   -#include <topsurf/descriptor.h>
2   -#include <topsurf/topsurf.h>
3   -#include <mm_plugin.h>
4   -
5   -#include "common/opencvutils.h"
6   -#include "common/qtutils.h"
7   -#include "common/resource.h"
8   -
9   -using namespace cv;
10   -using namespace mm;
11   -using namespace std;
12   -
13   -class TopSurfInitializer : public Initializer
14   -{
15   - void initialize() const
16   - {
17   - Globals.Abbreviations.insert("TopSurf", "Open!TopSurfExtract(40000):TopSurfCompare");
18   - Globals.Abbreviations.insert("TopSurfM", "Open!TopSurfExtract(1000000):TopSurfCompare");
19   - Globals.Abbreviations.insert("TopSurfKNN", "Open!TopSurfExtract+TopSurfKNN");
20   - Globals.Abbreviations.insert("DocumentClassification", "TopSurfKNN");
21   - }
22   -
23   - void finalize() const {}
24   -};
25   -
26   -MM_REGISTER(Initializer, TopSurfInitializer, false)
27   -
28   -
29   -class TopSurfResourceMaker : public ResourceMaker<TopSurf>
30   -{
31   - QString file;
32   -
33   -public:
34   - TopSurfResourceMaker(const QString &dictionary)
35   - {
36   - file = Globals.SDKPath + "/models/topsurf/dictionary_" + dictionary;
37   - }
38   -
39   -private:
40   - TopSurf *make() const
41   - {
42   - TopSurf *topSurf = new TopSurf(256, 100);
43   - if (!topSurf->LoadDictionary(qPrintable(file)))
44   - qFatal("TopSurfResourceMaker::make failed to load dictionary.");
45   - return topSurf;
46   - }
47   -};
48   -
49   -
50   -/****
51   -TopSurfExtract
52   - Wrapper to TopSurf::ExtractDescriptor()
53   - B. Thomee, E.M. Bakker, and M.S. Lew, "TOP-SURF: a visual words toolkit",
54   - in Proceedings of the 18th ACM International Conference on Multimedia, pp. 1473-1476, Firenze, Italy, 2010.
55   -****/
56   -class TopSurfExtract : public UntrainableFeature
57   -{
58   - Q_OBJECT
59   - Q_PROPERTY(QString dictionary READ get_dictionary WRITE set_dictionary)
60   - MM_MEMBER(QString, dictionary)
61   -
62   - Resource<TopSurf> topSurfResource;
63   -
64   -public:
65   - TopSurfExtract() : topSurfResource(new TopSurfResourceMaker("10000")) {}
66   -
67   -private:
68   - void init()
69   - {
70   - topSurfResource.setResourceMaker(new TopSurfResourceMaker(dictionary));
71   - }
72   -
73   - void project(const Template &src, Template &dst) const
74   - {
75   - // Compute descriptor (not thread safe)
76   - TopSurf *topSurf = topSurfResource.acquire();
77   - TOPSURF_DESCRIPTOR descriptor;
78   - IplImage iplSrc = src.m();
79   - if (!topSurf->ExtractDescriptor(iplSrc, descriptor))
80   - qFatal("TopSurfExtract::project ExtractDescriptor failure.");
81   - topSurfResource.release(topSurf);
82   -
83   - // Copy descriptor and clean up
84   - unsigned char *data;
85   - int length;
86   - Descriptor2Array(descriptor, data, length);
87   - Mat m(1, length, CV_8UC1);
88   - memcpy(m.data, data, length);
89   - delete data;
90   - TopSurf::ReleaseDescriptor(descriptor);
91   - dst = m;
92   - }
93   -
94   -public:
95   - static QString args()
96   - {
97   - return "10000|20000|40000 dictionary = 10000";
98   - }
99   -
100   - static TopSurfExtract *make(const QStringList &args)
101   - {
102   - (void) args;
103   - return new TopSurfExtract();
104   - }
105   -};
106   -
107   -MM_REGISTER(Feature, TopSurfExtract, true)
108   -
109   -
110   -class TopSurfHist : public UntrainableFeature
111   -{
112   - Q_OBJECT
113   - Q_PROPERTY(int size READ get_size WRITE set_size)
114   - MM_MEMBER(int, size)
115   -
116   - void project(const Template &src, Template &dst) const
117   - {
118   - TOPSURF_DESCRIPTOR td;
119   - Array2Descriptor(src.m().data, td);
120   -
121   - Mat m(1, size, CV_32FC1);
122   - m.setTo(0);
123   - for (int i=0; i<td.count; i++)
124   - m.at<float>(0, td.visualword[i].identifier % size)++;
125   -
126   - TopSurf::ReleaseDescriptor(td);
127   - dst = m;
128   - }
129   -
130   -public:
131   - static QString args()
132   - {
133   - return "int size = 10000";
134   - }
135   -
136   - static TopSurfHist *make(const QStringList &args)
137   - {
138   - (void) args;
139   - return new TopSurfHist();
140   - }
141   -};
142   -
143   -MM_REGISTER(Feature, TopSurfHist, true)
144   -
145   -
146   -// Wrapper around TopSurf CompareDescriptors
147   -float TopSurfSimilarity(const Mat &a, const Mat &b, bool cosine)
148   -{
149   - TOPSURF_DESCRIPTOR tda, tdb;
150   - Array2Descriptor(a.data, tda);
151   - Array2Descriptor(b.data, tdb);
152   -
153   - float result;
154   - if (cosine) result = TopSurf::CompareDescriptorsCosine(tda, tdb);
155   - else result = TopSurf::CompareDescriptorsAbsolute(tda, tdb);
156   -
157   - TopSurf::ReleaseDescriptor(tda);
158   - TopSurf::ReleaseDescriptor(tdb);
159   - return result;
160   -}
161   -
162   -
163   -/****
164   -TopSurfCompare
165   - Wrapper to TopSurf_CompareDescriptors()
166   - B. Thomee, E.M. Bakker, and M.S. Lew, "TOP-SURF: a visual words toolkit",
167   - in Proceedings of the 18th ACM International Conference on Multimedia, pp. 1473-1476, Firenze, Italy, 2010.
168   -****/
169   -class TopSurfCompare : public ComparerBase
170   -{
171   - Q_OBJECT
172   - Q_PROPERTY(bool cosine READ get_cosine WRITE set_cosine)
173   - MM_MEMBER(bool, cosine)
174   -
175   - float compare(const Mat &a, const Mat &b) const
176   - {
177   - return TopSurfSimilarity(a, b, cosine);
178   - }
179   -
180   -public:
181   - static QString args()
182   - {
183   - return "bool cosine = 1";
184   - }
185   -
186   - static TopSurfCompare *make(const QStringList &args)
187   - {
188   - (void) args;
189   - return new TopSurfCompare();
190   - }
191   -};
192   -
193   -MM_REGISTER(Comparer, TopSurfCompare, true)
194   -
195   -
196   -/****
197   -TopSurfKNN
198   - KNN classifier for TopSurf features.
199   -****/
200   -class TopSurfKNN : public Feature
201   -{
202   - Q_OBJECT
203   - Q_PROPERTY(int k READ get_k WRITE set_k)
204   - Q_PROPERTY(bool cosine READ get_cosine WRITE set_cosine)
205   - MM_MEMBER(int, k)
206   - MM_MEMBER(bool, cosine)
207   -
208   - TemplateList data;
209   -
210   -private:
211   - void train(const TemplateList &data)
212   - {
213   - this->data = data;
214   - }
215   -
216   - void project(const Template &src, Template &dst) const
217   - {
218   - // Compute distance to each descriptor
219   - QList< QPair<float, int> > distances; // <distance, label>
220   - distances.reserve(data.size());
221   - foreach (const Template &t, data)
222   - distances.append(QPair<float, int>(TopSurfSimilarity(src, t, cosine), t.file.label()));
223   -
224   - // Find nearest neighbors
225   - qSort(distances);
226   - QHash<int, QPair<int, float> > counts; // <label, <count, cumulative distance>>
227   - for (int i=0; i<k; i++) {
228   - QPair<float,int> &distance = distances[i];
229   - QPair<int,float> &count = counts[distance.second];
230   - count.first++;
231   - count.second += distance.first;
232   - }
233   -
234   - // Find most occuring label
235   - int best_label = -1;
236   - int best_count = 0;
237   - float best_distance = numeric_limits<float>::max();
238   - foreach (int label, counts.keys()) {
239   - const QPair<int, float> &count = counts[label];
240   - if ((count.first > best_count) || ((count.first == best_count) && (count.second < best_distance))) {
241   - best_label = label;
242   - best_count = count.first;
243   - best_distance = count.second;
244   - }
245   - }
246   - assert(best_label != -1);
247   -
248   - // Measure confidence
249   - int rest_count = 0;
250   - float rest_distance = 0;
251   - foreach (int label, counts.keys()) {
252   - if (label != best_label) {
253   - const QPair<int, float> &count = counts[label];
254   - rest_count = count.first;
255   - rest_distance = count.second;
256   - }
257   - }
258   -
259   - dst = src;
260   - dst.file["Label"] = best_label;
261   - dst.file["Confidence"] = (float)best_count/(float)k;
262   - }
263   -
264   - void store(QDataStream &stream) const
265   - {
266   - stream << data;
267   - }
268   -
269   - void load(QDataStream &stream)
270   - {
271   - stream >> data;
272   - }
273   -
274   -public:
275   - static QString args()
276   - {
277   - return "int k, int cosine = 1";
278   - }
279   -
280   - static TopSurfKNN *make(const QStringList &args)
281   - {
282   - (void) args;
283   - return new TopSurfKNN();
284   - }
285   -};
286   -
287   -MM_REGISTER(Feature, TopSurfKNN, true)
288   -
289   -#include "topsurf.moc"
openbr/plugins/validate.cpp
... ... @@ -89,6 +89,7 @@ class CrossValidateTransform : public MetaTransform
89 89 }
90 90 } else if (partitions[j] == i) {
91 91 partitionedData.removeAt(j);
  92 + j--;
92 93 } else j--;
93 94 }
94 95 // Train on the remaining templates
... ...
openbr/plugins/yubico.cmake deleted
1   -set(BR_WITH_YUBICO OFF CACHE BOOL "Build YubiKey authentication")
2   -
3   -if(${BR_WITH_YUBICO})
4   - find_package(YubiKey REQUIRED) # For decrypting YubiKeys
5   - find_package(YKPers REQUIRED) # For reading YubiKeys
6   -
7   - install(FILES ${YUBIKEY_LICENSE} RENAME YubiKey DESTINATION share/openbr/licenses)
8   - install(FILES ${YKPERS_LICENSE} RENAME YKPers DESTINATION share/openbr/licenses)
9   - install(FILES ${YKPERS_RULES} DESTINATION share/openbr)
10   -
11   - set(BR_THIRDPARTY_SRC ${BR_THIRDPARTY_SRC} ${YUBIKEY_SRC} ${YKPERS_SRC} plugins/yubico.cpp)
12   - set(BR_THIRDPARTY_LIBS ${BR_THIRDPARTY_LIBS} ${YKPERS_LIBS})
13   -endif()
openbr/plugins/yubico.cpp deleted
1   -#include <mm_plugin.h>
2   -#include <yubikey.h>
3   -#include <ykdef.h>
4   -#include <ykpers.h>
5   -#include <stdlib.h>
6   -#include <time.h>
7   -
8   -/****
9   -YubiKey Challenge-Response Authentication
10   -
11   -To configure YubiKeys for mm usage:
12   -1) Download the cross platform personalization tool from http://yubico.com/personalization-tool.
13   -2) Insert YubiKey and launch the personalization tool (may require sudo access).
14   -3) Click "Challenge-Response Mode".
15   -4) Click "Yubico OTP".
16   -5) Select "Configuration Slot 2"
17   -6) In the Private Identity text box enter "21 92 78 11 55 8a".
18   -7) In the Secret Key text box enter "e7 32 df 49 f3 87 e6 89 04 d2 03 6a 59 ad b7 2f".
19   -8) Click "Write Configuration".
20   -9) Done!
21   -
22   -Unix implementation derived from "ykchalresp.c" in ykpers repository.
23   -Windows implementation derived from "MFCTestDlg.cpp" in Yubikey Client API installer.
24   -
25   -!!! Attention Linux Users !!!
26   -cp trunk/3rdparty/ykpers-1.6.3/70-yubikey.rules /etc/udev/rules.d
27   -
28   -!!! Attention Windows Users !!!
29   -Install Yubikey Client API.
30   -****/
31   -
32   -using namespace mm;
33   -
34   -static int challenge_response(YK_KEY *yk, int slot,
35   - unsigned char *challenge, unsigned int len,
36   - bool hmac, bool may_block, bool verbose, unsigned char output_buf[(SHA1_MAX_BLOCK_SIZE * 2) + 1])
37   -{
38   - unsigned char response[64];
39   - int yk_cmd;
40   - unsigned int flags = 0;
41   - unsigned int response_len = 0;
42   - unsigned int expect_bytes = 0;
43   -
44   - memset(response, 0, sizeof(response));
45   -
46   - if (may_block)
47   - flags |= YK_FLAG_MAYBLOCK;
48   -
49   - if (verbose) {
50   - fprintf(stderr, "Sending %i bytes %s challenge to slot %i\n", len, (hmac == true) ? "HMAC" : "Yubico", slot);
51   - //_yk_hexdump(challenge, len);
52   - }
53   -
54   - switch(slot) {
55   - case 1:
56   - yk_cmd = (hmac == true) ? SLOT_CHAL_HMAC1 : SLOT_CHAL_OTP1;
57   - break;
58   - case 2:
59   - yk_cmd = (hmac == true) ? SLOT_CHAL_HMAC2 : SLOT_CHAL_OTP2;
60   - break;
61   - }
62   -
63   - if (!yk_write_to_key(yk, yk_cmd, challenge, len))
64   - return 0;
65   -
66   - if (verbose) {
67   - fprintf(stderr, "Reading response...\n");
68   - }
69   -
70   - /* HMAC responses are 160 bits, Yubico 128 */
71   - expect_bytes = (hmac == true) ? 20 : 16;
72   -
73   - if (! yk_read_response_from_key(yk, slot, flags,
74   - &response, sizeof(response),
75   - expect_bytes,
76   - &response_len))
77   - return 0;
78   -
79   - if (hmac && response_len > 20)
80   - response_len = 20;
81   - if (! hmac && response_len > 16)
82   - response_len = 16;
83   -
84   - memset(output_buf, 0, SHA1_MAX_BLOCK_SIZE * 2 + 1);
85   - if (hmac) {
86   - yubikey_hex_encode((char *)output_buf, (char *)response, response_len);
87   - } else {
88   - yubikey_modhex_encode((char *)output_buf, (char *)response, response_len);
89   - }
90   - // printf("%s\n", output_buf);
91   -
92   - return 1;
93   -}
94   -
95   -/*!
96   - * \ingroup initializers
97   - * \brief Initialize yubikey
98   - * \author Josh Klontz \cite jklontz
99   - */
100   -class YubiKey : public Initializer
101   -{
102   - Q_OBJECT
103   -
104   - void initialize() const
105   - {
106   - // Read from device
107   - YK_KEY *yk = 0;
108   -
109   - if (!yk_init())
110   - qFatal("YubiKey::initialize yk_init failure.");
111   -
112   - if (!(yk = yk_open_first_key()))
113   - qFatal("Could not connect to license.");
114   -
115   - // Challenge value is arbitrary
116   - srand(time(NULL));
117   - uint8_t challenge[6] = {rand()%255, rand()%255, rand()%255, rand()%255, rand()%255, rand()%255};
118   - unsigned char output_buf[(SHA1_MAX_BLOCK_SIZE * 2) + 1];
119   - if (!challenge_response(yk, 2, challenge, 6, false, true, false, output_buf))
120   - qFatal("YubiKey::initialize challenge_response failure.");
121   -
122   - if (yk && !yk_close_key(yk))
123   - qFatal("YubiKey::initialize yk_close_key failure.");
124   -
125   - if (!yk_release())
126   - qFatal("YubiKey::initialize yk_release failure.");
127   -
128   - // Check response
129   - // Our Secret Key! Shhh...
130   - const uint8_t key[YUBIKEY_KEY_SIZE] = {0xe7, 0x32, 0xdf, 0x49, 0xf3, 0x87, 0xe6, 0x89, 0x04, 0xd2, 0x03, 0x6a, 0x59, 0xad, 0xb7, 0x2f};
131   - yubikey_token_st out;
132   - yubikey_parse(output_buf, key, &out);
133   -
134   - // Our Private Identity! Shhh...
135   - uint8_t uid[YUBIKEY_UID_SIZE] = {0x21, 0x92, 0x78, 0x11, 0x55, 0x8a};
136   - if ((uid[0] != (out.uid[0] ^ challenge[0])) ||
137   - (uid[1] != (out.uid[1] ^ challenge[1])) ||
138   - (uid[2] != (out.uid[2] ^ challenge[2])) ||
139   - (uid[3] != (out.uid[3] ^ challenge[3])) ||
140   - (uid[4] != (out.uid[4] ^ challenge[4])) ||
141   - (uid[5] != (out.uid[5] ^ challenge[5])))
142   - qFatal("Invalid license.");
143   - }
144   -
145   - void finalize() const
146   - {
147   - // Nothing to do
148   - }
149   -};
150   -
151   -MM_REGISTER(Initializer,YubiKey,"")
152   -
153   -#include "yubico.moc"
share/openbr/cmake/FindCT8.cmake deleted
1   -# ================================================================
2   -# The CT8 CMake configuration file
3   -#
4   -# Usage from an external project:
5   -# In your CMakeLists.txt, add these lines:
6   -#
7   -# find_package(CT8 REQUIRED)
8   -# target_link_libraries(MY_TARGET ${CT8_LIBS})
9   -# ================================================================
10   -
11   -set(CT8_DIR "CT8_DIR-NOTFOUND" CACHE PATH "Cognitec FaceVACS 8.x directory")
12   -
13   -
14   -if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
15   - set(ARCH_STRING x86_64)
16   -else("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
17   - set(ARCH_STRING x86_32)
18   -endif("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
19   -
20   -if(DEFINED MSVC80)
21   - set(COMP_DIR_EXT "msc_8.0-ipp_crtdll")
22   -elseif(DEFINED MSVC90)
23   - set(COMP_DIR_EXT "msc_9.0-ipp_crtdll")
24   -elseif(DEFINED MSVC10)
25   - set(COMP_DIR_EXT "msc_10.0-ipp_crtdll")
26   -elseif(CMAKE_HOST_APPLE)
27   - set(COMP_DIR_EXT "gcc-4.2-ipp")
28   -else()
29   - set(COMP_DIR_EXT "gcc-4.3-ipp")
30   -endif()
31   -
32   -set(CT8_DIR_LIB ${CT8_DIR}/lib/${ARCH_STRING}/${COMP_DIR_EXT} )
33   -set(CT8_LIBRARY_RELEASE libfrsdk-8.6.0)
34   -set(CT8_LIBRARY_DEBUG libfrsdk-8.6.0d)
35   -
36   -include_directories(${CT8_DIR}/include)
37   -link_directories(${CT8_DIR_LIB} ${CT8_DIR_LIB}_g)
share/openbr/cmake/FindFST3.cmake deleted
1   -find_path(FST3_DIR _src_criteria/classifier.hpp ${CMAKE_SOURCE_DIR}/3rdparty/*)
2   -mark_as_advanced(FST3_DIR)
3   -include_directories(${FST3_DIR}/_src_criteria ${FST3_DIR}/_src_dataio ${FST3_DIR}/_src_global ${FST3_DIR}/_src_search)
4   -set(FST3_SRC ${FST3_DIR}/_src_global/global.cpp)
share/openbr/cmake/FindPP4.cmake deleted
1   -# ================================================================
2   -# The PP4 CMake configuration file
3   -#
4   -# Usage from an external project:
5   -# In your CMakeLists.txt, add these lines:
6   -#
7   -# find_package(PP4 REQUIRED)
8   -# target_link_libraries(MY_TARGET ${PP4_LIBS})
9   -# ================================================================
10   -
11   -find_path(PP4_DIR include/pittpatt_nc_sdk.h ${CMAKE_SOURCE_DIR}/3rdparty/*)
12   -include_directories(${PP4_DIR}/include)
13   -link_directories(${PP4_DIR}/lib)
14   -set(PP4_LIBS pittpatt_nc_sdk
15   - pittpatt_raw_image
16   - pittpatt_raw_image_io
17   - pittpatt_recognition_core
18   - pittpatt_video_io)
share/openbr/cmake/FindTopSurf.cmake deleted
1   -find_path(TOPSURF_DIR topsurf/topsurf.h ${CMAKE_SOURCE_DIR}/3rdparty/*)
2   -mark_as_advanced(TOPSURF_DIR)
3   -include_directories(${TOPSURF_DIR})
4   -aux_source_directory(${TOPSURF_DIR}/topsurf TOPSURF_SRC)
5   -aux_source_directory(${TOPSURF_DIR}/topsurf/flann TOPSURF_FLANN_SRC)
share/openbr/cmake/FindYubiKey.cmake deleted
1   -find_path(YUBIKEY_DIR yubikey.h ${CMAKE_SOURCE_DIR}/3rdparty/*)
2   -mark_as_advanced(YUBIKEY_DIR)
3   -include_directories(${YUBIKEY_DIR})
4   -if(MSVC)
5   - include_directories(${YUBIKEY_DIR}/stdbool)
6   -endif()
7   -
8   -if(NOT TARGET yubikey)
9   - set(YUBIKEY_SRC ${YUBIKEY_DIR}/ykaes.c ${YUBIKEY_DIR}/ykcrc.c ${YUBIKEY_DIR}/ykhex.c ${YUBIKEY_DIR}/ykmodhex.c ${YUBIKEY_DIR}/yktoken.c)
10   - if(WIN32)
11   - set_source_files_properties(${YUBIKEY_SRC} PROPERTIES LANGUAGE CXX)
12   - endif()
13   -endif()
14   -
15   -set(YUBIKEY_LICENSE ${YUBIKEY_DIR}/COPYING)
share/openbr/cmake/FindpHash.cmake deleted
1   -find_path(PHASH_DIR src/pHash.h ${CMAKE_SOURCE_DIR}/3rdparty/*)
2   -mark_as_advanced(PHASH_DIR)
3   -include_directories(${PHASH_DIR} ${PHASH_DIR}/src)
4   -set(PHASH_SRC ${PHASH_DIR}/src/pHash.cpp ${PHASH_DIR}/src/ph_fft.c)
1   -Subproject commit dccddf4dd3a5239911807beeec39308f8890b1e4
  1 +Subproject commit a73d51013ea05f263e88a28539393159fff2183e
... ...