Commit e3b533a3541db3043b4a8a8ffc5a7d176397d5db

Authored by sklum
2 parents 4f45b576 d449a28c

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

openbr/core/eval.cpp
... ... @@ -411,35 +411,82 @@ static QStringList computeDetectionResults(const QList<ResolvedDetection> &detec
411 411 QString getDetectKey(const TemplateList &templates)
412 412 {
413 413 const File &f = templates.first().file;
414   - foreach (const QString &key, f.localKeys())
  414 + foreach (const QString &key, f.localKeys()) {
  415 + // first check for single detections
415 416 if (!f.get<QRectF>(key, QRectF()).isNull())
416 417 return key;
  418 + }
  419 + // and then multiple
  420 + if (!f.rects().empty())
  421 + return "Rects";
417 422 return "";
418 423 }
419 424  
420   -float EvalDetection(const QString &predictedGallery, const QString &truthGallery, const QString &csv)
  425 +bool detectKeyIsList(QString key, const TemplateList &templates)
421 426 {
422   - qDebug("Evaluating detection of %s against %s", qPrintable(predictedGallery), qPrintable(truthGallery));
423   - const TemplateList predicted(TemplateList::fromGallery(predictedGallery));
424   - const TemplateList truth(TemplateList::fromGallery(truthGallery));
  427 + return templates.first().file.get<QRectF>(key, QRectF()).isNull();
  428 +}
  429 +
  430 +// return a list of detections whether the template holds
  431 +// multiple detections or a single detection
  432 +QList<Detection> getDetections(QString key, const Template &t, bool isList, bool isTruth)
  433 +{
  434 + File f = t.file;
  435 + QList<Detection> dets;
  436 + if (isList) {
  437 + QList<QRectF> rects = f.rects();
  438 + QList<float> confidences = f.getList<float>("Confidences", QList<float>());
  439 + if (!isTruth && rects.size() != confidences.size())
  440 + qFatal("You don't have enough confidence. I mean, your detections don't all have confidence measures.");
  441 + for (int i=0; i<rects.size(); i++) {
  442 + if (isTruth)
  443 + dets.append(Detection(rects.at(i)));
  444 + else
  445 + dets.append(Detection(rects.at(i), confidences.at(i)));
  446 + }
  447 + } else {
  448 + if (isTruth) {
  449 + dets.append(Detection(f.get<QRectF>(key)));
  450 + } else {
  451 + dets.append(Detection(f.get<QRectF>(key), f.get<float>("Confidence", -1)));
  452 + }
  453 + }
  454 + return dets;
  455 +}
425 456  
  457 +QMap<QString, Detections> getDetections(const TemplateList &predicted, const TemplateList &truth)
  458 +{
426 459 // Figure out which metadata field contains a bounding box
427 460 QString truthDetectKey = getDetectKey(truth);
428 461 if (truthDetectKey.isEmpty()) qFatal("No suitable ground truth metadata key found.");
429   - QString predictedDetectKey = truthDetectKey;
430   - if (predicted.first().file.get<QRectF>(predictedDetectKey, QRectF()).isNull())
431   - predictedDetectKey = getDetectKey(predicted);
  462 + QString predictedDetectKey = getDetectKey(predicted);
432 463 if (predictedDetectKey.isEmpty()) qFatal("No suitable predicted metadata key found.");
433   -
434 464 qDebug("Using metadata key: %s%s",
435 465 qPrintable(predictedDetectKey),
436 466 qPrintable(predictedDetectKey == truthDetectKey ? QString() : "/"+truthDetectKey));
437 467  
438   - QMap<QString, Detections> allDetections; // Organized by file, QMap used to preserve order
439   - foreach (const Template &t, predicted)
440   - allDetections[t.file.baseName()].predicted.append(Detection(t.file.get<QRectF>(predictedDetectKey), t.file.get<float>("Confidence", -1)));
441   - foreach (const Template &t, truth)
442   - allDetections[t.file.baseName()].truth.append(Detection(t.file.get<QRectF>(truthDetectKey)));
  468 + QMap<QString, Detections> allDetections;
  469 + bool predKeyIsList = detectKeyIsList(predictedDetectKey, predicted);
  470 + bool truthKeyIsList = detectKeyIsList(truthDetectKey, truth);
  471 + foreach (const Template &t, predicted) {
  472 + QList<Detection> dets = getDetections(predictedDetectKey, t, predKeyIsList, false);
  473 + allDetections[t.file.baseName()].predicted.append(dets);
  474 + }
  475 + foreach (const Template &t, truth) {
  476 + QList<Detection> dets = getDetections(truthDetectKey, t, truthKeyIsList, true);
  477 + allDetections[t.file.baseName()].truth.append(dets);
  478 + }
  479 + return allDetections;
  480 +}
  481 +
  482 +float EvalDetection(const QString &predictedGallery, const QString &truthGallery, const QString &csv)
  483 +{
  484 + qDebug("Evaluating detection of %s against %s", qPrintable(predictedGallery), qPrintable(truthGallery));
  485 + const TemplateList predicted(TemplateList::fromGallery(predictedGallery));
  486 + const TemplateList truth(TemplateList::fromGallery(truthGallery));
  487 +
  488 + // Organized by file, QMap used to preserve order
  489 + QMap<QString, Detections> allDetections = getDetections(predicted, truth);
443 490  
444 491 QList<ResolvedDetection> resolvedDetections, falseNegativeDetections;
445 492 foreach (Detections detections, allDetections.values()) {
... ...
openbr/core/plot.cpp
... ... @@ -343,10 +343,10 @@ bool PlotDetection(const QStringList &amp;files, const File &amp;destination, bool show)
343 343 QString(" + theme(aspect.ratio=1)\n\n")));
344 344  
345 345 p.file.write(qPrintable(QString("ggplot(AverageOverlap, aes(x=%1, y=%2, label=round(X,3)), main=\"Average Overlap\") + geom_text() + theme_minimal()").arg(p.minor.size > 1 ? p.minor.header : "'X'", p.major.size > 1 ? p.major.header : "'Y'") +
346   - QString("%1%2\n\n").arg(p.minor.size > 1 ? "" : " + xlab(NULL)", p.major.size > 1 ? "" : "ylab(NULL)")));
  346 + QString("%1%2\n\n").arg(p.minor.size > 1 ? "" : " + xlab(NULL)", p.major.size > 1 ? "" : " + ylab(NULL)")));
347 347  
348 348 p.file.write(qPrintable(QString("ggplot(AverageOverlap, aes(x=%1, y=%2, fill=X)) + geom_tile() + scale_fill_continuous(\"Average Overlap\") + theme_minimal()").arg(p.minor.size > 1 ? p.minor.header : "'X'", p.major.size > 1 ? p.major.header : "'Y'") +
349   - QString("%1%2\n\n").arg(p.minor.size > 1 ? "" : " + xlab(NULL)", p.major.size > 1 ? "" : "ylab(NULL)")));
  349 + QString("%1%2\n\n").arg(p.minor.size > 1 ? "" : " + xlab(NULL)", p.major.size > 1 ? "" : " + ylab(NULL)")));
350 350  
351 351 return p.finalize(show);
352 352 }
... ...
openbr/core/qtutils.cpp
... ... @@ -399,13 +399,33 @@ void showFile(const QString &amp;file)
399 399  
400 400 QString toString(const QVariant &variant)
401 401 {
402   - if (variant.canConvert(QVariant::String)) return variant.toString();
403   - else if(variant.canConvert(QVariant::PointF)) return QString("(%1,%2)").arg(QString::number(qvariant_cast<QPointF>(variant).x()),
404   - QString::number(qvariant_cast<QPointF>(variant).y()));
405   - else if (variant.canConvert(QVariant::RectF)) return QString("(%1,%2,%3,%4)").arg(QString::number(qvariant_cast<QRectF>(variant).x()),
406   - QString::number(qvariant_cast<QRectF>(variant).y()),
407   - QString::number(qvariant_cast<QRectF>(variant).width()),
408   - QString::number(qvariant_cast<QRectF>(variant).height()));
  402 + if (variant.canConvert(QVariant::String))
  403 + return variant.toString();
  404 + else if(variant.canConvert(QVariant::PointF)) {
  405 + QPointF pt = qvariant_cast<QPointF>(variant);
  406 + return QString("(%1,%2)").arg(QString::number(pt.x()),
  407 + QString::number(pt.y()));
  408 + }
  409 + else if (variant.canConvert(QVariant::RectF)) {
  410 + QRectF rect = qvariant_cast<QRectF>(variant);
  411 + return QString("(%1,%2,%3,%4)").arg(QString::number(rect.x()),
  412 + QString::number(rect.y()),
  413 + QString::number(rect.width()),
  414 + QString::number(rect.height()));
  415 + }
  416 + else if (variant.canConvert(QVariant::List)) {
  417 + QString ret = QString("[");
  418 + bool first = true;
  419 + foreach (const QVariant &i, variant.toList()) {
  420 + if (!first)
  421 + ret += ",";
  422 + else
  423 + first = false;
  424 + ret += toString(i);
  425 + }
  426 + ret += "]";
  427 + return ret;
  428 + }
409 429 return QString();
410 430 }
411 431  
... ...
openbr/openbr_plugin.cpp
... ... @@ -19,6 +19,7 @@
19 19 #include <QFutureSynchronizer>
20 20 #include <QMetaProperty>
21 21 #include <QPointF>
  22 +#include <QProcess>
22 23 #include <QRect>
23 24 #include <QRegExp>
24 25 #include <QThreadPool>
... ... @@ -40,6 +41,12 @@
40 41 using namespace br;
41 42 using namespace cv;
42 43  
  44 +// Some globals used to transfer data to Context::messageHandler so that
  45 +// we can restart the process if we try and fail to create a QApplication.
  46 +static bool creating_qapp = false;
  47 +static int * argc_ptr = NULL;
  48 +static char ** argv_ptr = NULL;
  49 +
43 50 /* File - public methods */
44 51 // Note that the convention for displaying metadata is as follows:
45 52 // [] for lists in which argument order does not matter (e.g. [FTO=false, Index=0]),
... ... @@ -886,14 +893,28 @@ void br::Context::initialize(int &amp;argc, char *argv[], QString sdkPath, bool use_
886 893 break;
887 894 }
888 895 }
  896 +
  897 + qInstallMessageHandler(messageHandler);
  898 +
889 899 // We take in argc as a reference due to:
890 900 // https://bugreports.qt-project.org/browse/QTBUG-5637
891 901 // QApplication should be initialized before anything else.
892 902 // Since we can't ensure that it gets deleted last, we never delete it.
893 903 if (QCoreApplication::instance() == NULL) {
894 904 #ifndef BR_EMBEDDED
895   - if (use_gui) application = new QApplication(argc, argv);
896   - else application = new QCoreApplication(argc, argv);
  905 + if (use_gui) {
  906 + // Set up variables to be used in the message handler if this fails.
  907 + // Just so you know, we
  908 + creating_qapp = true;
  909 + argc_ptr = &argc;
  910 + argv_ptr = argv;
  911 +
  912 + application = new QApplication(argc, argv);
  913 + creating_qapp = false;
  914 + }
  915 + else {
  916 + application = new QCoreApplication(argc, argv);
  917 + }
897 918 #else
898 919 application = new QCoreApplication(argc, argv);
899 920 #endif
... ... @@ -919,7 +940,6 @@ void br::Context::initialize(int &amp;argc, char *argv[], QString sdkPath, bool use_
919 940 Globals->init(File());
920 941 Globals->useGui = use_gui;
921 942  
922   - qInstallMessageHandler(messageHandler);
923 943  
924 944 Common::seedRNG();
925 945  
... ... @@ -988,6 +1008,26 @@ void br::Context::messageHandler(QtMsgType type, const QMessageLogContext &amp;conte
988 1008 static QMutex generalLock;
989 1009 QMutexLocker locker(&generalLock);
990 1010  
  1011 + // If we are trying to create a QApplication, and get a fatal, then restart the process
  1012 + // with useGui set to 0.
  1013 + if (creating_qapp && type == QtFatalMsg)
  1014 + {
  1015 + // re-launch process with useGui = 0
  1016 + std::cout << "Failed to initialize gui, restarting with -useGui 0" << std::endl;
  1017 + QStringList arguments;
  1018 + arguments.append("-useGui");
  1019 + arguments.append("0");
  1020 + for (int i=1; i < *argc_ptr; i++)
  1021 + {
  1022 + arguments.append(argv_ptr[i]);
  1023 + }
  1024 + // QProcess::execute blocks until the other process completes.
  1025 + QProcess::execute(argv_ptr[0], arguments);
  1026 + // have to unlock this for some reason
  1027 + locker.unlock();
  1028 + std::exit(0);
  1029 + }
  1030 +
991 1031 QString txt;
992 1032 if (type == QtDebugMsg) {
993 1033 if (Globals->quiet) return;
... ...
openbr/openbr_plugin.h
... ... @@ -41,6 +41,8 @@
41 41 #include <QVector>
42 42 #include <opencv2/core/core.hpp>
43 43 #include <openbr/openbr.h>
  44 +#include <openbr/core/qtutils.h>
  45 +#include <openbr/core/opencvutils.h>
44 46  
45 47 /*!
46 48 * \defgroup cpp_plugin_sdk C++ Plugin SDK
... ... @@ -215,6 +217,14 @@ struct BR_EXPORT File
215 217 static QVariant parse(const QString &value); /*!< \brief Try to convert the QString to a QPointF or QRectF if possible. */
216 218 inline void set(const QString &key, const QVariant &value) { m_metadata.insert(key, value); } /*!< \brief Insert or overwrite the metadata key with the specified value. */
217 219 void set(const QString &key, const QString &value); /*!< \brief Insert or overwrite the metadata key with the specified value. */
  220 +
  221 + /*!< \brief Specialization for list type. Insert or overwrite the metadata key with the specified value. */
  222 + template <typename T>
  223 + void setList(const QString &key, const QList<T> &value)
  224 + {
  225 + set(key, QtUtils::toVariantList(value));
  226 + }
  227 +
218 228 inline void remove(const QString &key) { m_metadata.remove(key); } /*!< \brief Remove the metadata key. */
219 229  
220 230 /*!< \brief Returns a value for the key, throwing an error if the key does not exist. */
... ... @@ -253,6 +263,19 @@ struct BR_EXPORT File
253 263 return list;
254 264 }
255 265  
  266 + /*!< \brief Specialization for list type. Returns a list of type T for the key, returning \em defaultValue if the key does not exist or can't be converted. */
  267 + template <typename T>
  268 + QList<T> getList(const QString &key, const QList<T> defaultValue) const
  269 + {
  270 + if (!contains(key)) return defaultValue;
  271 + QList<T> list;
  272 + foreach (const QVariant &item, m_metadata[key].toList()) {
  273 + if (item.canConvert<T>()) list.append(item.value<T>());
  274 + else return defaultValue;
  275 + }
  276 + return list;
  277 + }
  278 +
256 279 /*!< \brief Returns the value for the specified key for every file in the list. */
257 280 template<class U>
258 281 static QList<QVariant> values(const QList<U> &fileList, const QString &key)
... ... @@ -292,9 +315,12 @@ struct BR_EXPORT File
292 315 QList<QRectF> namedRects() const; /*!< \brief Returns rects convertible from metadata values. */
293 316 QList<QRectF> rects() const; /*!< \brief Returns the file's rects list. */
294 317 void appendRect(const QRectF &rect); /*!< \brief Adds a rect to the file's rect list. */
  318 + void appendRect(const cv::Rect &rect) { appendRect(OpenCVUtils::fromRect(rect)); } /*!< \brief Adds a rect to the file's rect list. */
295 319 void appendRects(const QList<QRectF> &rects); /*!< \brief Adds rects to the file's rect list. */
  320 + void appendRects(const QList<cv::Rect> &rects) { appendRects(OpenCVUtils::fromRects(rects)); } /*!< \brief Adds rects to the file's rect list. */
296 321 inline void clearRects() { m_metadata["Rects"] = QList<QVariant>(); } /*!< \brief Clears the file's rect list. */
297 322 inline void setRects(const QList<QRectF> &rects) { clearRects(); appendRects(rects); } /*!< \brief Overwrites the file's rect list. */
  323 + inline void setRects(const QList<cv::Rect> &rects) { clearRects(); appendRects(rects); } /*!< \brief Overwrites the file's rect list. */
298 324  
299 325 private:
300 326 QMap<QString,QVariant> m_metadata;
... ...
openbr/plugins/gallery.cpp
... ... @@ -857,18 +857,21 @@ class FDDBGallery : public Gallery
857 857 for (int i=0; i<numDetects; i++) {
858 858 const QStringList detect = lines.takeFirst().split(' ');
859 859 Template t(fileName);
  860 + QList<QVariant> faceList; //to be consistent with slidingWindow
860 861 if (detect.size() == 5) { //rectangle
861   - t.file.set("Face", QRectF(detect[0].toFloat(), detect[1].toFloat(), detect[2].toFloat(), detect[3].toFloat()));
  862 + faceList.append(QRectF(detect[0].toFloat(), detect[1].toFloat(), detect[2].toFloat(), detect[3].toFloat()));
862 863 t.file.set("Confidence", detect[4].toFloat());
863 864 } else if (detect.size() == 6) { //ellipse
864 865 float x = detect[3].toFloat(),
865 866 y = detect[4].toFloat(),
866 867 radius = detect[1].toFloat();
867   - t.file.set("Face", QRectF(x - radius,y - radius,radius * 2.0, radius * 2.0));
  868 + faceList.append(QRectF(x - radius,y - radius,radius * 2.0, radius * 2.0));
868 869 t.file.set("Confidence", detect[5].toFloat());
869 870 } else {
870 871 qFatal("Unknown FDDB annotation format.");
871 872 }
  873 + t.file.set("Face", faceList);
  874 + t.file.set("Label",QString("face"));
872 875 templates.append(t);
873 876 }
874 877 }
... ...
openbr/plugins/landmarks.cpp
... ... @@ -113,7 +113,7 @@ class ProcrustesTransform : public Transform
113 113 // R(0,0), R(1,0), R(1,1), R(0,1), mean_x, mean_y, norm
114 114 QList<float> procrustesStats;
115 115 procrustesStats << R(0,0) << R(1,0) << R(1,1) << R(0,1) << mean[0] << mean[1] << norm;
116   - dst.file.set("ProcrustesStats",QtUtils::toVariantList(procrustesStats));
  116 + dst.file.setList<float>("ProcrustesStats",procrustesStats);
117 117  
118 118 if (warp) {
119 119 Eigen::MatrixXf dstMat = srcMat*R;
... ... @@ -273,7 +273,7 @@ class DelaunayTransform : public UntrainableTransform
273 273 dst.file.setRects(QList<QRectF>() << OpenCVUtils::fromRect(boundingBox));
274 274 } else dst = src;
275 275  
276   - dst.file.set("DelaunayTriangles", QtUtils::toVariantList(validTriangles));
  276 + dst.file.setList<QPointF>("DelaunayTriangles", validTriangles);
277 277 }
278 278 };
279 279  
... ...
openbr/plugins/regions.cpp
... ... @@ -53,7 +53,6 @@ class RectRegionsTransform : public UntrainableTransform
53 53 dst += m(Rect(x, y, width, height));
54 54 }
55 55 };
56   -
57 56 BR_REGISTER(Transform, RectRegionsTransform)
58 57  
59 58 /*!
... ...
openbr/plugins/slidingwindow.cpp
1 1 #include "openbr_internal.h"
2 2 #include "openbr/core/opencvutils.h"
3 3 #include "openbr/core/common.h"
  4 +#include "openbr/core/qtutils.h"
  5 +#include <opencv2/objdetect/objdetect.hpp>
  6 +#include <opencv2/imgproc/imgproc.hpp>
  7 +#include <opencv2/highgui/highgui.hpp>
4 8  
5 9 using namespace cv;
6 10  
  11 +// Because MSVC doesn't provide a round() function in math.h
  12 +static int round(float x) { return (floor(x + 0.5)); }
  13 +
7 14 namespace br
8 15 {
9 16  
... ... @@ -19,31 +26,65 @@ class SlidingWindowTransform : public Transform
19 26 Q_PROPERTY(br::Transform *transform READ get_transform WRITE set_transform RESET reset_transform STORED false)
20 27 Q_PROPERTY(int minSize READ get_minSize WRITE set_minSize RESET reset_minSize STORED false)
21 28 Q_PROPERTY(double scaleFactor READ get_scaleFactor WRITE set_scaleFactor RESET reset_scaleFactor STORED false)
22   - Q_PROPERTY(double stepSize READ get_stepSize WRITE set_stepSize RESET reset_stepSize STORED false)
  29 + Q_PROPERTY(int stepSize READ get_stepSize WRITE set_stepSize RESET reset_stepSize STORED false)
23 30 Q_PROPERTY(bool takeLargestScale READ get_takeLargestScale WRITE set_takeLargestScale RESET reset_takeLargestScale STORED false)
24 31 Q_PROPERTY(bool negSamples READ get_negSamples WRITE set_negSamples RESET reset_negSamples STORED false)
25 32 Q_PROPERTY(int negToPosRatio READ get_negToPosRatio WRITE set_negToPosRatio RESET reset_negToPosRatio STORED false)
26 33 Q_PROPERTY(double maxOverlap READ get_maxOverlap WRITE set_maxOverlap RESET reset_maxOverlap STORED false)
  34 + Q_PROPERTY(float aspectRatio READ get_aspectRatio WRITE set_aspectRatio RESET reset_aspectRatio STORED true)
  35 + Q_PROPERTY(int windowWidth READ get_windowWidth WRITE set_windowWidth RESET reset_windowWidth STORED false)
27 36 BR_PROPERTY(br::Transform *, transform, NULL)
28 37 BR_PROPERTY(int, minSize, 8)
29 38 BR_PROPERTY(double, scaleFactor, 0.75)
30   - BR_PROPERTY(double, stepSize, 1)
  39 + BR_PROPERTY(int, stepSize, 1)
31 40 BR_PROPERTY(bool, takeLargestScale, true)
32 41 BR_PROPERTY(bool, negSamples, true)
33 42 BR_PROPERTY(int, negToPosRatio, 1)
34 43 BR_PROPERTY(double, maxOverlap, 0)
  44 + BR_PROPERTY(float, aspectRatio, 1)
  45 + BR_PROPERTY(int, windowWidth, 24)
35 46  
36 47 public:
37 48 SlidingWindowTransform() : Transform(false, true) {}
38   -
39 49 private:
  50 +
40 51 void train(const TemplateList &data)
41 52 {
42 53 if (transform->trainable) {
  54 + double tempRatio = 0;
  55 + int ratioCnt = 0;
43 56 TemplateList full;
  57 +
  58 + //First find avg aspect ratio
  59 + foreach (const Template &tmpl, data) {
  60 + QList<Rect> posRects = OpenCVUtils::toRects(tmpl.file.rects());
  61 + foreach (const Rect &posRect, posRects) {
  62 + if (posRect.x + posRect.width >= tmpl.m().cols || posRect.y + posRect.height >= tmpl.m().rows || posRect.x < 0 || posRect.y < 0) {
  63 + continue;
  64 + }
  65 + tempRatio += (float)posRect.width / (float)posRect.height;
  66 + ratioCnt += 1;
  67 + }
  68 + }
  69 + aspectRatio = tempRatio / (double)ratioCnt;
  70 +
44 71 foreach (const Template &tmpl, data) {
45   - foreach (const Rect &rect, OpenCVUtils::toRects(tmpl.file.rects())) {
46   - Template pos(tmpl.file, Mat(tmpl, rect));
  72 + QList<Rect> posRects = OpenCVUtils::toRects(tmpl.file.rects());
  73 + QList<Rect> negRects;
  74 + foreach (Rect posRect, posRects) {
  75 +
  76 + //Adjust for training samples that have different aspect ratios
  77 + int diff = posRect.width - (int)((float) posRect.height * aspectRatio);
  78 + posRect.x += diff / 2;
  79 + posRect.width += diff;
  80 +
  81 + if (posRect.x + posRect.width >= tmpl.m().cols || posRect.y + posRect.height >= tmpl.m().rows || posRect.x < 0 || posRect.y < 0) {
  82 + continue;
  83 + }
  84 +
  85 + Mat scaledImg;
  86 + resize(Mat(tmpl, posRect), scaledImg, Size(windowWidth,round(windowWidth / aspectRatio)));
  87 + Template pos(tmpl.file, scaledImg);
47 88 full += pos;
48 89  
49 90 // add random negative samples
... ... @@ -57,9 +98,11 @@ private:
57 98 int maxSize = std::min(maxWidth, maxHeight);
58 99 int size = (maxSize <= minSize ? maxSize : Common::RandSample(1, maxSize, minSize)[0]);
59 100 Rect negRect(x, y, size, size);
60   - Rect intersect = negRect & rect;
61   - if (intersect.area() > maxOverlap*rect.area())
  101 + // the negative samples cannot overlap the positive at all
  102 + // but they may overlap with other negatives
  103 + if (overlaps(posRects, negRect, 0) || overlaps(negRects, negRect, maxOverlap))
62 104 continue;
  105 + negRects.append(negRect);
63 106 Template neg(tmpl.file, Mat(tmpl, negRect));
64 107 neg.file.set("Label", QString("neg"));
65 108 full += neg;
... ... @@ -72,6 +115,16 @@ private:
72 115 }
73 116 }
74 117  
  118 + bool overlaps(QList<Rect> posRects, Rect negRect, double overlap)
  119 + {
  120 + foreach (const Rect posRect, posRects) {
  121 + Rect intersect = negRect & posRect;
  122 + if (intersect.area() > overlap*posRect.area())
  123 + return true;
  124 + }
  125 + return false;
  126 + }
  127 +
75 128 void project(const Template &src, Template &dst) const
76 129 {
77 130 dst = src;
... ... @@ -79,17 +132,33 @@ private:
79 132 if (src.file.getBool("Train", false)) return;
80 133  
81 134 dst.file.clearRects();
82   - int rows = src.m().rows, cols = src.m().cols;
83   - for (double size=std::min(rows, cols); size>=minSize; size*=scaleFactor) {
84   - for (double y=0; y+size<rows; y+=(size*stepSize)) {
85   - for (double x=0; x+size<cols; x+=(size*stepSize)) {
86   - Rect window(x, y, size, size);
87   - Template windowMat(src.file, Mat(src.m(), window));
  135 + int rows = src.m().rows;
  136 + int cols = src.m().cols;
  137 + int windowHeight = (int) round((float) windowWidth / aspectRatio);
  138 + float startScale;
  139 + if ((cols / rows) > aspectRatio)
  140 + startScale = round((float) rows / (float) windowHeight);
  141 + else
  142 + startScale = round((float) cols / (float) windowWidth);
  143 +
  144 + for (float scale = startScale; scale >= 1.0; scale -= (1.0 - scaleFactor)) {
  145 + Mat scaleImg;
  146 + resize(src, scaleImg, Size(round(cols / scale), round(rows / scale)));
  147 +
  148 + for (double y = 0; y + windowHeight < scaleImg.rows; y += stepSize) {
  149 + for (double x = 0; x + windowWidth < scaleImg.cols; x += stepSize) {
  150 +qDebug() << "x=" << x << "\ty=" << y;
  151 + Rect window(x, y, windowWidth, windowHeight);
  152 + Template windowMat(src.file, Mat(scaleImg, window));
88 153 Template detect;
89 154 transform->project(windowMat, detect);
90 155 // the result will be in the Label
91   - if (detect.file.get<QString>(QString("Label")) == "pos") {
92   - dst.file.appendRect(OpenCVUtils::fromRect(window));
  156 + if (detect.file.get<QString>("Label") == "pos") {
  157 + dst.file.appendRect(QRectF((float) x * scale, (float) y * scale, (float) windowWidth * scale, (float) windowHeight * scale));
  158 + float confidence = detect.file.get<float>("Dist");
  159 + QList<float> confidences = dst.file.getList<float>("Confidences", QList<float>());
  160 + confidences.append(confidence);
  161 + dst.file.setList<float>("Confidences", confidences);
93 162 if (takeLargestScale) return;
94 163 }
95 164 }
... ... @@ -100,6 +169,36 @@ private:
100 169  
101 170 BR_REGISTER(Transform, SlidingWindowTransform)
102 171  
  172 +/*!
  173 + * \ingroup transforms
  174 + * \brief Detects objects with OpenCV's built-in HOG detection.
  175 + * \author Austin Blanton \cite imaus10
  176 + */
  177 +class HOGDetectTransform : public UntrainableTransform
  178 +{
  179 + Q_OBJECT
  180 +
  181 + HOGDescriptor hog;
  182 +
  183 + void init()
  184 + {
  185 + hog.setSVMDetector(HOGDescriptor::getDefaultPeopleDetector());
  186 + }
  187 +
  188 + void project(const Template &src, Template &dst) const
  189 + {
  190 + dst = src;
  191 + std::vector<Rect> objLocs;
  192 + QList<Rect> rects;
  193 + hog.detectMultiScale(src, objLocs);
  194 + foreach (const Rect &obj, objLocs)
  195 + rects.append(obj);
  196 + dst.file.setRects(rects);
  197 + }
  198 +};
  199 +
  200 +BR_REGISTER(Transform, HOGDetectTransform)
  201 +
103 202 } // namespace br
104 203  
105 204 #include "slidingwindow.moc"
... ...
openbr/plugins/svm.cpp
... ... @@ -103,6 +103,7 @@ class SVMTransform : public Transform
103 103 Q_PROPERTY(float gamma READ get_gamma WRITE set_gamma RESET reset_gamma STORED false)
104 104 Q_PROPERTY(QString inputVariable READ get_inputVariable WRITE set_inputVariable RESET reset_inputVariable STORED false)
105 105 Q_PROPERTY(QString outputVariable READ get_outputVariable WRITE set_outputVariable RESET reset_outputVariable STORED false)
  106 + Q_PROPERTY(bool returnDFVal READ get_returnDFVal WRITE set_returnDFVal RESET reset_returnDFVal STORED false)
106 107  
107 108 public:
108 109 enum Kernel { Linear = CvSVM::LINEAR,
... ... @@ -123,6 +124,7 @@ private:
123 124 BR_PROPERTY(float, gamma, -1)
124 125 BR_PROPERTY(QString, inputVariable, "")
125 126 BR_PROPERTY(QString, outputVariable, "")
  127 + BR_PROPERTY(bool, returnDFVal, false)
126 128  
127 129  
128 130 SVM svm;
... ... @@ -149,8 +151,17 @@ private:
149 151  
150 152 void project(const Template &src, Template &dst) const
151 153 {
  154 + if (returnDFVal && reverseLookup.size() > 2)
  155 + qFatal("Decision function for multiclass classification not implemented.");
  156 +
152 157 dst = src;
153   - float prediction = svm.predict(src.m().reshape(1, 1));
  158 + float prediction = svm.predict(src.m().reshape(1, 1), returnDFVal);
  159 + if (returnDFVal) {
  160 + dst.file.set("Dist", prediction);
  161 + // positive values ==> first class
  162 + // negative values ==> second class
  163 + prediction = prediction > 0 ? 0 : 1;
  164 + }
154 165 if (type == EPS_SVR || type == NU_SVR)
155 166 dst.file.set(outputVariable, prediction);
156 167 else
... ...