Commit ca4e68e31759076392e5e78419dd6210c5a64661
Fixed some DisplayGUI logic
Showing
3 changed files
with
142 additions
and
85 deletions
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,6 +10,7 @@ | ||
| 10 | * [MNIST](MNIST/README.md) | 10 | * [MNIST](MNIST/README.md) |
| 11 | * [PCSO](PCSO/README.md) | 11 | * [PCSO](PCSO/README.md) |
| 12 | * [KTH](KTH/README.md) | 12 | * [KTH](KTH/README.md) |
| 13 | +* [FDDB](FDDB/README.md) | ||
| 13 | 14 | ||
| 14 | For both practical and legal reasons we only include images for some of the datasets in this repository. | 15 | For both practical and legal reasons we only include images for some of the datasets in this repository. |
| 15 | Researchers should contact the respective owners of the other datasets in order to obtain a copy. | 16 | Researchers should contact the respective owners of the other datasets in order to obtain a copy. |
openbr/plugins/gui.cpp
| @@ -18,6 +18,109 @@ using namespace cv; | @@ -18,6 +18,109 @@ using namespace cv; | ||
| 18 | 18 | ||
| 19 | namespace br | 19 | namespace br |
| 20 | { | 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 | + | ||
| 21 | QImage toQImage(const Mat &mat) | 124 | QImage toQImage(const Mat &mat) |
| 22 | { | 125 | { |
| 23 | // Convert to 8U depth | 126 | // Convert to 8U depth |
| @@ -229,18 +332,14 @@ public: | @@ -229,18 +332,14 @@ public: | ||
| 229 | public slots: | 332 | public slots: |
| 230 | void showImage(const QPixmap & input) | 333 | void showImage(const QPixmap & input) |
| 231 | { | 334 | { |
| 232 | - pixmap = input; | ||
| 233 | - foreach(const QString& label, keys) { | ||
| 234 | - QLineEdit *edit = new QLineEdit; | ||
| 235 | - fields.append(edit); | ||
| 236 | - QFormLayout *form = new QFormLayout; | ||
| 237 | - form->addRow(label, edit); | ||
| 238 | - inputLayout->addLayout(form); | ||
| 239 | - } | 335 | + hide(); |
| 240 | 336 | ||
| 241 | - show(); | 337 | + createForm(); |
| 338 | + pixmap = input; | ||
| 242 | label.setPixmap(pixmap); | 339 | label.setPixmap(pixmap); |
| 243 | label.setFixedSize(input.size()); | 340 | label.setFixedSize(input.size()); |
| 341 | + | ||
| 342 | + show(); | ||
| 244 | } | 343 | } |
| 245 | 344 | ||
| 246 | QStringList waitForButtonPress() | 345 | QStringList waitForButtonPress() |
| @@ -249,7 +348,10 @@ public slots: | @@ -249,7 +348,10 @@ public slots: | ||
| 249 | wait.wait(&lock); | 348 | wait.wait(&lock); |
| 250 | 349 | ||
| 251 | QStringList values; | 350 | QStringList values; |
| 252 | - for(int i = 0; i<fields.size(); i++) values.append(fields.at(i)->text()); | 351 | + for(int i = 0; i<fields.size(); i++) { |
| 352 | + values.append(fields.at(i)->text()); | ||
| 353 | + fields[i]->clear(); | ||
| 354 | + } | ||
| 253 | return values; | 355 | return values; |
| 254 | } | 356 | } |
| 255 | 357 | ||
| @@ -267,6 +369,19 @@ public slots: | @@ -267,6 +369,19 @@ public slots: | ||
| 267 | 369 | ||
| 268 | private: | 370 | private: |
| 269 | 371 | ||
| 372 | + void createForm() | ||
| 373 | + { | ||
| 374 | + if (fields.size() != keys.size()) { | ||
| 375 | + foreach(const QString& label, keys) { | ||
| 376 | + QLineEdit *edit = new QLineEdit; | ||
| 377 | + fields.append(edit); | ||
| 378 | + QFormLayout *form = new QFormLayout; | ||
| 379 | + form->addRow(label, edit); | ||
| 380 | + inputLayout->addLayout(form); | ||
| 381 | + } | ||
| 382 | + } | ||
| 383 | + } | ||
| 384 | + | ||
| 270 | QWidget *centralWidget; | 385 | QWidget *centralWidget; |
| 271 | QStringList keys; | 386 | QStringList keys; |
| 272 | QList<QLineEdit*> fields; | 387 | QList<QLineEdit*> fields; |
| @@ -280,75 +395,6 @@ private: | @@ -280,75 +395,6 @@ private: | ||
| 280 | 395 | ||
| 281 | }; | 396 | }; |
| 282 | 397 | ||
| 283 | -// I want a template class that doesn't look like a template class | ||
| 284 | -class NominalCreation | ||
| 285 | -{ | ||
| 286 | -public: | ||
| 287 | - virtual ~NominalCreation() {} | ||
| 288 | - virtual void creation()=0; | ||
| 289 | -}; | ||
| 290 | - | ||
| 291 | -// Putting the template on a subclass means we can maintain a pointer that | ||
| 292 | -// doesn't include T in its type. | ||
| 293 | -template<typename T> | ||
| 294 | -class ActualCreation : public NominalCreation | ||
| 295 | -{ | ||
| 296 | -public: | ||
| 297 | - T * basis; | ||
| 298 | - | ||
| 299 | - void creation() | ||
| 300 | - { | ||
| 301 | - basis = new T(); | ||
| 302 | - } | ||
| 303 | -}; | ||
| 304 | - | ||
| 305 | -// We want to create a QLabel subclass on the main thread, but are running in another thread. | ||
| 306 | -// We cannot move QWidget subclasses to a different thread (obviously that would be crazy), but | ||
| 307 | -// we can create one of these, and move it to the main thread, and then use it to create the object | ||
| 308 | -// we want. | ||
| 309 | -// Additional fact: QObject subclasses cannot be template classes. | ||
| 310 | -class MainThreadCreator : public QObject | ||
| 311 | -{ | ||
| 312 | - Q_OBJECT | ||
| 313 | -public: | ||
| 314 | - | ||
| 315 | - MainThreadCreator() | ||
| 316 | - { | ||
| 317 | - this->moveToThread(QApplication::instance()->thread()); | ||
| 318 | - | ||
| 319 | - connect(this, SIGNAL(needCreation()), this, SLOT(createThing()), Qt::BlockingQueuedConnection); | ||
| 320 | - } | ||
| 321 | - | ||
| 322 | - // While this cannot be a template class, it can still have a template method. | ||
| 323 | - template<typename T> | ||
| 324 | - T * getItem() | ||
| 325 | - { | ||
| 326 | - if (QThread::currentThread() == QApplication::instance()->thread()) | ||
| 327 | - return new T(); | ||
| 328 | - | ||
| 329 | - ActualCreation<T> * actualWorker; | ||
| 330 | - actualWorker = new ActualCreation<T> (); | ||
| 331 | - worker = actualWorker; | ||
| 332 | - | ||
| 333 | - emit needCreation(); | ||
| 334 | - | ||
| 335 | - T * output = actualWorker->basis; | ||
| 336 | - delete actualWorker; | ||
| 337 | - return output; | ||
| 338 | - } | ||
| 339 | - | ||
| 340 | - NominalCreation * worker; | ||
| 341 | - | ||
| 342 | -signals: | ||
| 343 | - void needCreation(); | ||
| 344 | - | ||
| 345 | -public slots: | ||
| 346 | - void createThing() | ||
| 347 | - { | ||
| 348 | - worker->creation(); | ||
| 349 | - } | ||
| 350 | -}; | ||
| 351 | - | ||
| 352 | /*! | 398 | /*! |
| 353 | * \ingroup transforms | 399 | * \ingroup transforms |
| 354 | * \brief Displays templates in a GUI pop-up window using QT. | 400 | * \brief Displays templates in a GUI pop-up window using QT. |
| @@ -379,12 +425,6 @@ public: | @@ -379,12 +425,6 @@ public: | ||
| 379 | 425 | ||
| 380 | void train(const TemplateList &data) { (void) data; } | 426 | void train(const TemplateList &data) { (void) data; } |
| 381 | 427 | ||
| 382 | - void project(const TemplateList &src, TemplateList &dst) const | ||
| 383 | - { | ||
| 384 | - Transform * non_const = (ShowTransform *) this; | ||
| 385 | - non_const->projectUpdate(src,dst); | ||
| 386 | - } | ||
| 387 | - | ||
| 388 | void projectUpdate(const TemplateList &src, TemplateList &dst) | 428 | void projectUpdate(const TemplateList &src, TemplateList &dst) |
| 389 | { | 429 | { |
| 390 | dst = src; | 430 | dst = src; |
| @@ -659,6 +699,11 @@ public: | @@ -659,6 +699,11 @@ public: | ||
| 659 | BR_REGISTER(Transform, SurveyTransform) | 699 | BR_REGISTER(Transform, SurveyTransform) |
| 660 | 700 | ||
| 661 | 701 | ||
| 702 | +/*! | ||
| 703 | + * \ingroup transforms | ||
| 704 | + * \brief Limits the frequency of projects going through this transform to the input targetFPS | ||
| 705 | + * \author Charles Otto \cite caotto | ||
| 706 | + */ | ||
| 662 | class FPSLimit : public TimeVaryingTransform | 707 | class FPSLimit : public TimeVaryingTransform |
| 663 | { | 708 | { |
| 664 | Q_OBJECT | 709 | Q_OBJECT |
| @@ -709,6 +754,12 @@ protected: | @@ -709,6 +754,12 @@ protected: | ||
| 709 | }; | 754 | }; |
| 710 | BR_REGISTER(Transform, FPSLimit) | 755 | BR_REGISTER(Transform, FPSLimit) |
| 711 | 756 | ||
| 757 | +/*! | ||
| 758 | + * \ingroup transforms | ||
| 759 | + * \brief Calculates the average FPS of projects going through this transform, stores the result in AvgFPS | ||
| 760 | + * Reports an average FPS from the initialization of this transform onwards. | ||
| 761 | + * \author Charles Otto \cite caotto | ||
| 762 | + */ | ||
| 712 | class FPSCalc : public TimeVaryingTransform | 763 | class FPSCalc : public TimeVaryingTransform |
| 713 | { | 764 | { |
| 714 | Q_OBJECT | 765 | Q_OBJECT |