Commit d0f1b236473da269610f25b7a9ecc6ff754c6754
Merge branch 'master' of https://github.com/biometrics/openbr into training_structure
Showing
36 changed files
with
478 additions
and
1667 deletions
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 &files, const File &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 &files, const File &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 &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 &files, const File &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<T> |
| 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 = ".*", 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
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
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
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
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