Commit fe93bf86a3efddb38f58d028a352041efe8a8a54

Authored by Scott Klum
2 parents 7669f325 89f4ce30

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

openbr/core/core.cpp
@@ -19,6 +19,7 @@ @@ -19,6 +19,7 @@
19 #include "bee.h" 19 #include "bee.h"
20 #include "common.h" 20 #include "common.h"
21 #include "qtutils.h" 21 #include "qtutils.h"
  22 +#include "../plugins/openbr_internal.h"
22 23
23 using namespace br; 24 using namespace br;
24 25
@@ -44,6 +45,14 @@ struct AlgorithmCore @@ -44,6 +45,14 @@ struct AlgorithmCore
44 qDebug("Training on %s%s", qPrintable(input.flat()), 45 qDebug("Training on %s%s", qPrintable(input.flat()),
45 model.isEmpty() ? "" : qPrintable(" to " + model)); 46 model.isEmpty() ? "" : qPrintable(" to " + model));
46 47
  48 + QScopedPointer<Transform> trainingWrapper(Transform::make("DirectStream([Identity])", NULL));
  49 + CompositeTransform * downcast = dynamic_cast<CompositeTransform *>(trainingWrapper.data());
  50 + if (downcast == NULL)
  51 + qFatal("downcast failed?");
  52 + downcast->transforms[0] = this->transform.data();
  53 +
  54 + downcast->init();
  55 +
47 TemplateList data(TemplateList::fromGallery(input)); 56 TemplateList data(TemplateList::fromGallery(input));
48 57
49 // set the Train bool metadata, in case a Transform's project 58 // set the Train bool metadata, in case a Transform's project
@@ -56,7 +65,7 @@ struct AlgorithmCore @@ -56,7 +65,7 @@ struct AlgorithmCore
56 65
57 QTime time; time.start(); 66 QTime time; time.start();
58 qDebug("Training Enrollment"); 67 qDebug("Training Enrollment");
59 - transform->train(data); 68 + downcast->train(data);
60 69
61 if (!distance.isNull()) { 70 if (!distance.isNull()) {
62 qDebug("Projecting Enrollment"); 71 qDebug("Projecting Enrollment");
@@ -114,74 +123,64 @@ struct AlgorithmCore @@ -114,74 +123,64 @@ struct AlgorithmCore
114 123
115 FileList enroll(File input, File gallery = File()) 124 FileList enroll(File input, File gallery = File())
116 { 125 {
  126 + FileList files;
  127 +
117 qDebug("Enrolling %s%s", qPrintable(input.flat()), 128 qDebug("Enrolling %s%s", qPrintable(input.flat()),
118 gallery.isNull() ? "" : qPrintable(" to " + gallery.flat())); 129 gallery.isNull() ? "" : qPrintable(" to " + gallery.flat()));
119 130
120 - FileList fileList;  
121 if (gallery.name.isEmpty()) { 131 if (gallery.name.isEmpty()) {
122 if (input.name.isEmpty()) return FileList(); 132 if (input.name.isEmpty()) return FileList();
123 else gallery = getMemoryGallery(input); 133 else gallery = getMemoryGallery(input);
124 } 134 }
  135 + TemplateList data(TemplateList::fromGallery(input));
125 136
126 - QScopedPointer<Gallery> g(Gallery::make(gallery));  
127 - if (g.isNull()) qFatal("Null gallery!");  
128 -  
129 - do {  
130 - fileList.clear();  
131 -  
132 - if (gallery.contains("read") || gallery.contains("cache"))  
133 - fileList = g->files();  
134 -  
135 - if (!fileList.isEmpty() && gallery.contains("cache"))  
136 - return fileList;  
137 -  
138 - const TemplateList i(TemplateList::fromGallery(input));  
139 - if (i.isEmpty()) return fileList; // Nothing to enroll  
140 -  
141 - if (transform.isNull()) qFatal("Null transform.");  
142 - const int blocks = Globals->blocks(i.size());  
143 - Globals->currentStep = 0;  
144 - Globals->totalSteps = i.size();  
145 - Globals->startTime.start();  
146 -  
147 - const bool noDuplicates = gallery.contains("noDuplicates");  
148 - QStringList fileNames = noDuplicates ? fileList.names() : QStringList();  
149 - const int subBlockSize = 4*std::max(1, Globals->parallelism);  
150 - const int numSubBlocks = ceil(1.0*Globals->blockSize/subBlockSize);  
151 - int totalCount = 0, failureCount = 0;  
152 - double totalBytes = 0;  
153 - for (int block=0; block<blocks; block++) {  
154 - for (int subBlock = 0; subBlock<numSubBlocks; subBlock++) {  
155 - TemplateList data = i.mid(block*Globals->blockSize + subBlock*subBlockSize, subBlockSize);  
156 - if (data.isEmpty()) break;  
157 - if (noDuplicates)  
158 - for (int i=data.size()-1; i>=0; i--)  
159 - if (fileNames.contains(data[i].file.name))  
160 - data.removeAt(i);  
161 - const int numFiles = data.size();  
162 -  
163 - data >> *transform;  
164 -  
165 - g->writeBlock(data);  
166 - const FileList newFiles = data.files();  
167 - fileList.append(newFiles);  
168 -  
169 - totalCount += newFiles.size();  
170 - failureCount += newFiles.failures();  
171 - totalBytes += data.bytes<double>();  
172 - Globals->currentStep += numFiles;  
173 - Globals->printStatus(); 137 + if (gallery.contains("append"))
  138 + {
  139 + // Remove any templates which are already in the gallery
  140 + QScopedPointer<Gallery> g(Gallery::make(gallery));
  141 + files = g->files();
  142 + QSet<QString> nameSet = QSet<QString>::fromList(files.names());
  143 + for (int i = data.size() - 1; i>=0; i--) {
  144 + if (nameSet.contains(data[i].file.name))
  145 + {
  146 + data.removeAt(i);
174 } 147 }
175 } 148 }
  149 + }
  150 +
  151 + if (data.empty())
  152 + return files;
  153 +
  154 + // Trust me, this makes complete sense.
  155 + // We're just going to make a pipe with a placeholder first transform
  156 + QString pipeDesc = "Identity+GalleryOutput("+gallery.flat()+")+ProgressCounter("+QString::number(data.length())+")+Discard";
  157 + QScopedPointer<Transform> basePipe(Transform::make(pipeDesc,NULL));
  158 +
  159 + CompositeTransform * downcast = dynamic_cast<CompositeTransform *>(basePipe.data());
  160 + if (downcast == NULL)
  161 + qFatal("downcast failed?");
  162 +
  163 + // replace that placeholder with the current algorithm
  164 + downcast->transforms[0] = this->transform.data();
  165 +
  166 + // call init on the pipe to collapse the algorithm (if its top level is a pipe)
  167 + downcast->init();
  168 +
  169 + // Next, we make a Stream (with placeholder transform)
  170 + QString streamDesc = "Stream(Identity, readMode=DistributeFrames)";
  171 + QScopedPointer<Transform> baseStream(Transform::make(streamDesc, NULL));
  172 + WrapperTransform * wrapper = dynamic_cast<WrapperTransform *> (baseStream.data());
  173 +
  174 + // replace that placeholder with the pipe we built
  175 + wrapper->transform = downcast;
  176 +
  177 + // and get the final stream's stages by reinterpreting the pipe. Perfectly straightforward.
  178 + wrapper->init();
176 179
177 - const float speed = 1000 * Globals->totalSteps / Globals->startTime.elapsed() / std::max(1, abs(Globals->parallelism));  
178 - if (!Globals->quiet && (Globals->totalSteps > 1))  
179 - fprintf(stderr, "\rTIME ELAPSED (MINS) %f SPEED=%.1e SIZE=%.4g FAILURES=%d/%d \n",  
180 - Globals->startTime.elapsed()/1000./60.,speed, totalBytes/totalCount, failureCount, totalCount);  
181 - Globals->totalSteps = 0;  
182 - } while (input.getBool("infinite")); 180 + wrapper->projectUpdate(data,data);
183 181
184 - return fileList; 182 + files.append(data.files());
  183 + return files;
185 } 184 }
186 185
187 void enroll(TemplateList &data) 186 void enroll(TemplateList &data)
@@ -312,8 +311,6 @@ private: @@ -312,8 +311,6 @@ private:
312 if ((words.size() < 1) || (words.size() > 2)) qFatal("Invalid algorithm format."); 311 if ((words.size() < 1) || (words.size() > 2)) qFatal("Invalid algorithm format.");
313 //! [Parsing the algorithm description] 312 //! [Parsing the algorithm description]
314 313
315 - if (description.getBool("distribute", true))  
316 - words[0] = "DistributeTemplate(" + words[0] + ")";  
317 314
318 //! [Creating the template generation and comparison methods] 315 //! [Creating the template generation and comparison methods]
319 transform = QSharedPointer<Transform>(Transform::make(words[0], NULL)); 316 transform = QSharedPointer<Transform>(Transform::make(words[0], NULL));
openbr/core/opencvutils.cpp
@@ -300,6 +300,16 @@ QList&lt;QRectF&gt; OpenCVUtils::fromRects(const QList&lt;Rect&gt; &amp;cvRects) @@ -300,6 +300,16 @@ QList&lt;QRectF&gt; OpenCVUtils::fromRects(const QList&lt;Rect&gt; &amp;cvRects)
300 return qRects; 300 return qRects;
301 } 301 }
302 302
  303 +bool OpenCVUtils::overlaps(const QList<Rect> &posRects, const Rect &negRect, double overlap)
  304 +{
  305 + foreach (const Rect &posRect, posRects) {
  306 + Rect intersect = negRect & posRect;
  307 + if (intersect.area() > overlap*posRect.area())
  308 + return true;
  309 + }
  310 + return false;
  311 +}
  312 +
303 QDataStream &operator<<(QDataStream &stream, const Mat &m) 313 QDataStream &operator<<(QDataStream &stream, const Mat &m)
304 { 314 {
305 // Write header 315 // Write header
openbr/core/opencvutils.h
@@ -87,6 +87,7 @@ namespace OpenCVUtils @@ -87,6 +87,7 @@ namespace OpenCVUtils
87 QRectF fromRect(const cv::Rect &cvRect); 87 QRectF fromRect(const cv::Rect &cvRect);
88 QList<cv::Rect> toRects(const QList<QRectF> &qRects); 88 QList<cv::Rect> toRects(const QList<QRectF> &qRects);
89 QList<QRectF> fromRects(const QList<cv::Rect> &cvRects); 89 QList<QRectF> fromRects(const QList<cv::Rect> &cvRects);
  90 + bool overlaps(const QList<cv::Rect> &posRects, const cv::Rect &negRect, double overlap);
90 91
91 int getFourcc(); 92 int getFourcc();
92 } 93 }
openbr/core/qtutils.cpp
@@ -427,6 +427,17 @@ QString toString(const QVariantList &amp;variantList) @@ -427,6 +427,17 @@ QString toString(const QVariantList &amp;variantList)
427 return QString(); 427 return QString();
428 } 428 }
429 429
  430 +QString toTime(int s)
  431 +{
  432 + int h = s / (60*60);
  433 + int m = (s - h*60*60) / 60;
  434 + s = (s - h*60*60 - m*60);
  435 +
  436 + const QChar fillChar = QLatin1Char('0');
  437 +
  438 + return QString("%1:%2:%3").arg(h,2,10,fillChar).arg(m,2,10,fillChar).arg(s,2,10,fillChar);
  439 +}
  440 +
430 float euclideanLength(const QPointF &point) 441 float euclideanLength(const QPointF &point)
431 { 442 {
432 return sqrt(pow(point.x(), 2) + pow(point.y(), 2)); 443 return sqrt(pow(point.x(), 2) + pow(point.y(), 2));
openbr/core/qtutils.h
@@ -65,6 +65,7 @@ namespace QtUtils @@ -65,6 +65,7 @@ namespace QtUtils
65 QPointF toPoint(const QString &string, bool *ok = NULL); 65 QPointF toPoint(const QString &string, bool *ok = NULL);
66 QRectF toRect(const QString &string, bool *ok = NULL); 66 QRectF toRect(const QString &string, bool *ok = NULL);
67 QStringList naturalSort(const QStringList &strings); 67 QStringList naturalSort(const QStringList &strings);
  68 + QString toTime(int s);
68 69
69 /**** Process Utilities ****/ 70 /**** Process Utilities ****/
70 bool runRScript(const QString &file); 71 bool runRScript(const QString &file);
openbr/openbr_plugin.cpp
@@ -839,10 +839,7 @@ void br::Context::printStatus() @@ -839,10 +839,7 @@ void br::Context::printStatus()
839 const float p = progress(); 839 const float p = progress();
840 if (p < 1) { 840 if (p < 1) {
841 int s = timeRemaining(); 841 int s = timeRemaining();
842 - int h = s / (60*60);  
843 - int m = (s - h*60*60) / 60;  
844 - s = (s - h*60*60 - m*60);  
845 - fprintf(stderr, "%05.2f%% REMAINING=%02d:%02d:%02d COUNT=%g \r", 100 * p, h, m, s, totalSteps); 842 + fprintf(stderr, "%05.2f%% REMAINING=%s COUNT=%g \r", 100 * p, QtUtils::toTime(s/1000.0f).toStdString().c_str(), totalSteps);
846 } 843 }
847 } 844 }
848 845
@@ -1156,6 +1153,17 @@ Gallery *Gallery::make(const File &amp;file) @@ -1156,6 +1153,17 @@ Gallery *Gallery::make(const File &amp;file)
1156 return gallery; 1153 return gallery;
1157 } 1154 }
1158 1155
  1156 +// Default init -- if the file contains "append", read the existing
  1157 +// data and immediately write it
  1158 +void Gallery::init()
  1159 +{
  1160 + if (file.exists() && file.contains("append"))
  1161 + {
  1162 + TemplateList data = this->read();
  1163 + this->writeBlock(data);
  1164 + }
  1165 +}
  1166 +
1159 /* Transform - public methods */ 1167 /* Transform - public methods */
1160 Transform::Transform(bool _independent, bool _trainable) 1168 Transform::Transform(bool _independent, bool _trainable)
1161 { 1169 {
openbr/openbr_plugin.h
@@ -1060,6 +1060,7 @@ public: @@ -1060,6 +1060,7 @@ public:
1060 void writeBlock(const TemplateList &templates); /*!< \brief Serialize a template list. */ 1060 void writeBlock(const TemplateList &templates); /*!< \brief Serialize a template list. */
1061 virtual void write(const Template &t) = 0; /*!< \brief Serialize a template. */ 1061 virtual void write(const Template &t) = 0; /*!< \brief Serialize a template. */
1062 static Gallery *make(const File &file); /*!< \brief Make a gallery to/from a file on disk. */ 1062 static Gallery *make(const File &file); /*!< \brief Make a gallery to/from a file on disk. */
  1063 + void init();
1063 1064
1064 private: 1065 private:
1065 QSharedPointer<Gallery> next; 1066 QSharedPointer<Gallery> next;
openbr/plugins/algorithms.cpp
@@ -38,7 +38,7 @@ class AlgorithmsInitializer : public Initializer @@ -38,7 +38,7 @@ class AlgorithmsInitializer : public Initializer
38 Globals->abbreviations.insert("MedianFace", "Open!Cascade(FrontalFace)+ASEFEyes+Affine(256,256,0.37,0.45)+Center(Median)"); 38 Globals->abbreviations.insert("MedianFace", "Open!Cascade(FrontalFace)+ASEFEyes+Affine(256,256,0.37,0.45)+Center(Median)");
39 Globals->abbreviations.insert("BlurredFaceDetection", "Open+LimitSize(1024)+SkinMask/(Cvt(Gray)+GradientMask)+And+Morph(Erode,16)+LargestConvexArea"); 39 Globals->abbreviations.insert("BlurredFaceDetection", "Open+LimitSize(1024)+SkinMask/(Cvt(Gray)+GradientMask)+And+Morph(Erode,16)+LargestConvexArea");
40 Globals->abbreviations.insert("DrawFaceDetection", "Open+Cascade(FrontalFace)!ASEFEyes+Draw"); 40 Globals->abbreviations.insert("DrawFaceDetection", "Open+Cascade(FrontalFace)!ASEFEyes+Draw");
41 - Globals->abbreviations.insert("ShowFaceDetection", "DrawFaceDetection!Show[distribute=false]"); 41 + Globals->abbreviations.insert("ShowFaceDetection", "DrawFaceDetection!Show");
42 Globals->abbreviations.insert("OpenBR", "FaceRecognition"); 42 Globals->abbreviations.insert("OpenBR", "FaceRecognition");
43 Globals->abbreviations.insert("GenderEstimation", "GenderClassification"); 43 Globals->abbreviations.insert("GenderEstimation", "GenderClassification");
44 Globals->abbreviations.insert("AgeEstimation", "AgeRegression"); 44 Globals->abbreviations.insert("AgeEstimation", "AgeRegression");
openbr/plugins/gallery.cpp
@@ -73,6 +73,11 @@ class arffGallery : public Gallery @@ -73,6 +73,11 @@ class arffGallery : public Gallery
73 arffFile.write(qPrintable(OpenCVUtils::matrixToStringList(t).join(','))); 73 arffFile.write(qPrintable(OpenCVUtils::matrixToStringList(t).join(',')));
74 arffFile.write(qPrintable(",'" + t.file.get<QString>("Label") + "'\n")); 74 arffFile.write(qPrintable(",'" + t.file.get<QString>("Label") + "'\n"));
75 } 75 }
  76 +
  77 + void init()
  78 + {
  79 + //
  80 + }
76 }; 81 };
77 82
78 BR_REGISTER(Gallery, arffGallery) 83 BR_REGISTER(Gallery, arffGallery)
@@ -94,7 +99,12 @@ class galGallery : public Gallery @@ -94,7 +99,12 @@ class galGallery : public Gallery
94 if (file.get<bool>("remove", false)) 99 if (file.get<bool>("remove", false))
95 gallery.remove(); 100 gallery.remove();
96 QtUtils::touchDir(gallery); 101 QtUtils::touchDir(gallery);
97 - if (!gallery.open(QFile::ReadWrite | QFile::Append)) 102 + QFile::OpenMode mode = QFile::ReadWrite;
  103 +
  104 + if (file.contains("append"))
  105 + mode |= QFile::Append;
  106 +
  107 + if (!gallery.open(mode))
98 qFatal("Can't open gallery: %s", qPrintable(gallery.fileName())); 108 qFatal("Can't open gallery: %s", qPrintable(gallery.fileName()));
99 stream.setDevice(&gallery); 109 stream.setDevice(&gallery);
100 } 110 }
@@ -579,6 +589,11 @@ class templateGallery : public Gallery @@ -579,6 +589,11 @@ class templateGallery : public Gallery
579 (void) t; 589 (void) t;
580 qFatal("No supported."); 590 qFatal("No supported.");
581 } 591 }
  592 +
  593 + void init()
  594 + {
  595 + //
  596 + }
582 }; 597 };
583 598
584 BR_REGISTER(Gallery, templateGallery) 599 BR_REGISTER(Gallery, templateGallery)
@@ -737,6 +752,11 @@ class dbGallery : public Gallery @@ -737,6 +752,11 @@ class dbGallery : public Gallery
737 (void) t; 752 (void) t;
738 qFatal("Not supported."); 753 qFatal("Not supported.");
739 } 754 }
  755 +
  756 + void init()
  757 + {
  758 + //
  759 + }
740 }; 760 };
741 761
742 BR_REGISTER(Gallery, dbGallery) 762 BR_REGISTER(Gallery, dbGallery)
@@ -790,6 +810,11 @@ class googleGallery : public Gallery @@ -790,6 +810,11 @@ class googleGallery : public Gallery
790 (void) t; 810 (void) t;
791 qFatal("Not supported."); 811 qFatal("Not supported.");
792 } 812 }
  813 +
  814 + void init()
  815 + {
  816 + //
  817 + }
793 }; 818 };
794 819
795 BR_REGISTER(Gallery, googleGallery) 820 BR_REGISTER(Gallery, googleGallery)
@@ -883,6 +908,11 @@ class FDDBGallery : public Gallery @@ -883,6 +908,11 @@ class FDDBGallery : public Gallery
883 (void) t; 908 (void) t;
884 qFatal("Not implemented."); 909 qFatal("Not implemented.");
885 } 910 }
  911 +
  912 + void init()
  913 + {
  914 + //
  915 + }
886 }; 916 };
887 917
888 BR_REGISTER(Gallery, FDDBGallery) 918 BR_REGISTER(Gallery, FDDBGallery)
@@ -927,6 +957,11 @@ class landmarksGallery : public Gallery @@ -927,6 +957,11 @@ class landmarksGallery : public Gallery
927 (void) t; 957 (void) t;
928 qFatal("Not implemented."); 958 qFatal("Not implemented.");
929 } 959 }
  960 +
  961 + void init()
  962 + {
  963 + //
  964 + }
930 }; 965 };
931 966
932 BR_REGISTER(Gallery, landmarksGallery) 967 BR_REGISTER(Gallery, landmarksGallery)
openbr/plugins/misc.cpp
@@ -14,10 +14,12 @@ @@ -14,10 +14,12 @@
14 * limitations under the License. * 14 * limitations under the License. *
15 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 15 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16 16
  17 +#include <QElapsedTimer>
17 #include <QRegularExpression> 18 #include <QRegularExpression>
18 #include <opencv2/highgui/highgui.hpp> 19 #include <opencv2/highgui/highgui.hpp>
19 #include "openbr_internal.h" 20 #include "openbr_internal.h"
20 #include "openbr/core/opencvutils.h" 21 #include "openbr/core/opencvutils.h"
  22 +#include "openbr/core/qtutils.h"
21 23
22 using namespace cv; 24 using namespace cv;
23 25
@@ -517,6 +519,106 @@ class EventTransform : public UntrainableMetaTransform @@ -517,6 +519,106 @@ class EventTransform : public UntrainableMetaTransform
517 }; 519 };
518 BR_REGISTER(Transform, EventTransform) 520 BR_REGISTER(Transform, EventTransform)
519 521
  522 +
  523 +class GalleryOutputTransform : public TimeVaryingTransform
  524 +{
  525 + Q_OBJECT
  526 +
  527 + Q_PROPERTY(QString outputString READ get_outputString WRITE set_outputString RESET reset_outputString STORED false)
  528 + BR_PROPERTY(QString, outputString, "")
  529 +
  530 + void projectUpdate(const TemplateList &src, TemplateList &dst)
  531 + {
  532 + if (src.empty())
  533 + return;
  534 + dst = src;
  535 + writer->writeBlock(dst);
  536 + }
  537 +
  538 + void train(const TemplateList& data)
  539 + {
  540 + (void) data;
  541 + }
  542 + ;
  543 + void init()
  544 + {
  545 + writer = QSharedPointer<Gallery>(Gallery::make(outputString));
  546 + }
  547 +
  548 + QSharedPointer<Gallery> writer;
  549 +public:
  550 + GalleryOutputTransform() : TimeVaryingTransform(false,false) {}
  551 +};
  552 +
  553 +BR_REGISTER(Transform, GalleryOutputTransform)
  554 +
  555 +class ProgressCounterTransform : public TimeVaryingTransform
  556 +{
  557 + Q_OBJECT
  558 +
  559 + Q_PROPERTY(int totalTemplates READ get_totalTemplates WRITE set_totalTemplates RESET reset_totalTemplates STORED false)
  560 + BR_PROPERTY(int, totalTemplates, 1)
  561 +
  562 + void projectUpdate(const TemplateList &src, TemplateList &dst)
  563 + {
  564 + dst = src;
  565 + qint64 elapsed = timer.elapsed();
  566 + calls++;
  567 + set_calls++;
  568 + // updated every 10 seconds
  569 + if (elapsed > 5 * 1000) {
  570 + float f_elapsed = elapsed / 1000.0f;
  571 + // remaining calls (according to our input variable)
  572 + int remaining = totalTemplates - calls;
  573 + // calls / second
  574 + float speed = set_calls / f_elapsed;
  575 +
  576 + float p = 100 * float(calls) / totalTemplates;
  577 +
  578 + // seconds remaining
  579 + int s = float(remaining) / speed;
  580 +
  581 + fprintf(stderr, "%05.2f%% ELAPSED=%s REMAINING=%s COUNT=%g \r", p, QtUtils::toTime(Globals->startTime.elapsed()/1000.0f).toStdString().c_str(), QtUtils::toTime(s).toStdString().c_str(), float(calls));
  582 +
  583 + timer.start();
  584 + set_calls = 0;
  585 + }
  586 +
  587 +
  588 + return;
  589 + }
  590 +
  591 + void train(const TemplateList& data)
  592 + {
  593 + (void) data;
  594 + }
  595 +
  596 + void finalize(TemplateList & data)
  597 + {
  598 + (void) data;
  599 + float p = 100 * float(calls) / totalTemplates;
  600 + qDebug("%05.2f%% ELAPSED=%s REMAINING=%s COUNT=%g \r", p, QtUtils::toTime(Globals->startTime.elapsed()/1000.0f).toStdString().c_str(), QtUtils::toTime(0).toStdString().c_str(), float(calls));
  601 + }
  602 +
  603 + void init()
  604 + {
  605 + calls = 0;
  606 + set_calls = 0;
  607 + timer.start();
  608 + Globals->startTime.start();
  609 + }
  610 +
  611 +public:
  612 + ProgressCounterTransform() : TimeVaryingTransform(false,false) {}
  613 + bool initialized;
  614 + QElapsedTimer timer;
  615 + qint64 calls;
  616 + qint64 set_calls;
  617 +
  618 +};
  619 +
  620 +BR_REGISTER(Transform, ProgressCounterTransform)
  621 +
520 } 622 }
521 623
522 #include "misc.moc" 624 #include "misc.moc"
openbr/plugins/openbr_internal.h
@@ -154,6 +154,53 @@ protected: @@ -154,6 +154,53 @@ protected:
154 } 154 }
155 }; 155 };
156 156
  157 +/*!
  158 + * \brief Interface for transforms that act as decorators of another transform
  159 + */
  160 +class BR_EXPORT WrapperTransform : public TimeVaryingTransform
  161 +{
  162 + Q_OBJECT
  163 +public:
  164 + WrapperTransform(bool independent = true) : TimeVaryingTransform(independent)
  165 + {
  166 + }
  167 +
  168 + Q_PROPERTY(br::Transform *transform READ get_transform WRITE set_transform RESET reset_transform STORED false)
  169 + BR_PROPERTY(br::Transform *, transform, NULL)
  170 +
  171 + bool timeVarying() const { return transform->timeVarying(); }
  172 +
  173 + void project(const Template &src, Template &dst) const
  174 + {
  175 + transform->project(src,dst);
  176 + }
  177 +
  178 + void projectUpdate(const Template &src, Template &dst)
  179 + {
  180 + transform->projectUpdate(src,dst);
  181 + }
  182 + void projectUpdate(const TemplateList & src, TemplateList & dst)
  183 + {
  184 + transform->projectUpdate(src,dst);
  185 + }
  186 +
  187 + void train(const QList<TemplateList> & data)
  188 + {
  189 + transform->train(data);
  190 + }
  191 +
  192 + virtual void finalize(TemplateList & output)
  193 + {
  194 + transform->finalize(output);
  195 + }
  196 +
  197 + void init()
  198 + {
  199 + if (transform)
  200 + this->trainable = transform->trainable;
  201 + }
  202 +
  203 +};
157 204
158 /*! 205 /*!
159 * \brief A MetaTransform that aggregates some sub-transforms 206 * \brief A MetaTransform that aggregates some sub-transforms
openbr/plugins/plugins.cmake
@@ -5,6 +5,8 @@ @@ -5,6 +5,8 @@
5 # BR_THIRDPARTY_LIBS - Additional libaries needed by a plugin 5 # BR_THIRDPARTY_LIBS - Additional libaries needed by a plugin
6 6
7 # Also look for CMake modules in the thirdparty plugins folder(s) 7 # Also look for CMake modules in the thirdparty plugins folder(s)
  8 +set(BR_THIRDPARTY_PLUGINS_DIR CACHE PATH "")
  9 +mark_as_advanced(BR_THIRDPARTY_PLUGINS_DIR)
8 set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${BR_THIRDPARTY_PLUGINS_DIR}) 10 set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${BR_THIRDPARTY_PLUGINS_DIR})
9 11
10 foreach(DIR ${BR_THIRDPARTY_PLUGINS_DIR}) 12 foreach(DIR ${BR_THIRDPARTY_PLUGINS_DIR})
openbr/plugins/slidingwindow.cpp
@@ -38,7 +38,7 @@ static float getAspectRatio(const TemplateList &amp;data) @@ -38,7 +38,7 @@ static float getAspectRatio(const TemplateList &amp;data)
38 * Discards negative detections. 38 * Discards negative detections.
39 * \author Austin Blanton \cite imaus10 39 * \author Austin Blanton \cite imaus10
40 */ 40 */
41 -class SlidingWindowTransform : public Transform 41 +class SlidingWindowTransform : public MetaTransform
42 { 42 {
43 Q_OBJECT 43 Q_OBJECT
44 Q_PROPERTY(br::Transform *transform READ get_transform WRITE set_transform RESET reset_transform STORED false) 44 Q_PROPERTY(br::Transform *transform READ get_transform WRITE set_transform RESET reset_transform STORED false)
@@ -52,8 +52,6 @@ class SlidingWindowTransform : public Transform @@ -52,8 +52,6 @@ class SlidingWindowTransform : public Transform
52 BR_PROPERTY(int, windowWidth, 24) 52 BR_PROPERTY(int, windowWidth, 24)
53 BR_PROPERTY(float, threshold, 0) 53 BR_PROPERTY(float, threshold, 0)
54 54
55 -public:  
56 - SlidingWindowTransform() : Transform(false, true) {}  
57 private: 55 private:
58 int windowHeight; 56 int windowHeight;
59 57
@@ -62,7 +60,7 @@ private: @@ -62,7 +60,7 @@ private:
62 float aspectRatio = data.first().file.get<float>("aspectRatio", -1); 60 float aspectRatio = data.first().file.get<float>("aspectRatio", -1);
63 if (aspectRatio == -1) 61 if (aspectRatio == -1)
64 aspectRatio = getAspectRatio(data); 62 aspectRatio = getAspectRatio(data);
65 - windowHeight = (int) qRound((float) windowWidth / aspectRatio); 63 + windowHeight = qRound(windowWidth / aspectRatio);
66 if (transform->trainable) { 64 if (transform->trainable) {
67 transform->train(data); 65 transform->train(data);
68 } 66 }
@@ -97,16 +95,89 @@ private: @@ -97,16 +95,89 @@ private:
97 } 95 }
98 dst.file.setList<float>("Confidences", confidences); 96 dst.file.setList<float>("Confidences", confidences);
99 } 97 }
  98 +
  99 + void store(QDataStream &stream) const
  100 + {
  101 + transform->store(stream);
  102 + stream << windowHeight;
  103 + }
  104 +
  105 + void load(QDataStream &stream)
  106 + {
  107 + transform->load(stream);
  108 + stream >> windowHeight;
  109 + }
100 }; 110 };
101 111
102 BR_REGISTER(Transform, SlidingWindowTransform) 112 BR_REGISTER(Transform, SlidingWindowTransform)
103 113
  114 +static TemplateList cropTrainingSamples(const TemplateList &data, const float aspectRatio, const int minSize = 0, const float maxOverlap = 0.5, const int negToPosRatio = 1)
  115 +{
  116 + TemplateList result;
  117 + foreach (const Template &tmpl, data) {
  118 + QList<Rect> posRects = OpenCVUtils::toRects(tmpl.file.rects());
  119 + QList<Rect> negRects;
  120 + for (int i=0; i<posRects.size(); i++) {
  121 + Rect &posRect = posRects[i];
  122 +
  123 + // Adjust for training samples that have different aspect ratios
  124 + const int diff = posRect.width - int(posRect.height * aspectRatio);
  125 + posRect.x += diff / 2;
  126 + posRect.width += diff;
  127 +
  128 + // Ignore samples larger than the image
  129 + if ((posRect.x + posRect.width >= tmpl.m().cols) ||
  130 + (posRect.y + posRect.height >= tmpl.m().rows) ||
  131 + (posRect.x < 0) ||
  132 + (posRect.y < 0))
  133 + continue;
  134 +
  135 + result += Template(tmpl.file, Mat(tmpl, posRect));
  136 +
  137 + // Add random negative samples
  138 + Mat m = tmpl.m();
  139 + int sample = 0;
  140 + while (sample < negToPosRatio) {
  141 + const int x = rand() % m.cols;
  142 + const int y = rand() % m.rows;
  143 + const int maxWidth = m.cols - x;
  144 + const int maxHeight = m.rows - y;
  145 + if (maxWidth <= minSize || maxHeight <= minSize)
  146 + continue;
  147 +
  148 + int height;
  149 + int width;
  150 + if (aspectRatio > (float) maxWidth / (float) maxHeight) {
  151 + width = rand() % (maxWidth - minSize) + minSize;
  152 + height = qRound(width / aspectRatio);
  153 + } else {
  154 + height = rand() % (maxHeight - minSize) + minSize;
  155 + width = qRound(height * aspectRatio);
  156 + }
  157 + Rect negRect(x, y, width, height);
  158 +
  159 + // The negative samples cannot overlap the positive samples at
  160 + // all, but they may partially overlap with other negatives.
  161 + if (OpenCVUtils::overlaps(posRects, negRect, 0) ||
  162 + OpenCVUtils::overlaps(negRects, negRect, maxOverlap))
  163 + continue;
  164 +
  165 + result += Template(tmpl.file, Mat(tmpl, negRect));
  166 + result.last().file.set("Label", QString("neg"));
  167 + sample++;
  168 + }
  169 + }
  170 + }
  171 +
  172 + return result;
  173 +}
  174 +
104 /*! 175 /*!
105 * \ingroup transforms 176 * \ingroup transforms
106 * \brief . 177 * \brief .
107 * \author Austin Blanton \cite imaus10 178 * \author Austin Blanton \cite imaus10
108 */ 179 */
109 -class BuildScalesTransform : public Transform 180 +class BuildScalesTransform : public MetaTransform
110 { 181 {
111 Q_OBJECT 182 Q_OBJECT
112 Q_PROPERTY(br::Transform *transform READ get_transform WRITE set_transform RESET reset_transform STORED false) 183 Q_PROPERTY(br::Transform *transform READ get_transform WRITE set_transform RESET reset_transform STORED false)
@@ -117,7 +188,6 @@ class BuildScalesTransform : public Transform @@ -117,7 +188,6 @@ class BuildScalesTransform : public Transform
117 Q_PROPERTY(int minSize READ get_minSize WRITE set_minSize RESET reset_minSize STORED false) 188 Q_PROPERTY(int minSize READ get_minSize WRITE set_minSize RESET reset_minSize STORED false)
118 Q_PROPERTY(double maxOverlap READ get_maxOverlap WRITE set_maxOverlap RESET reset_maxOverlap STORED false) 189 Q_PROPERTY(double maxOverlap READ get_maxOverlap WRITE set_maxOverlap RESET reset_maxOverlap STORED false)
119 Q_PROPERTY(float minScale READ get_minScale WRITE set_minScale RESET reset_minScale STORED false) 190 Q_PROPERTY(float minScale READ get_minScale WRITE set_minScale RESET reset_minScale STORED false)
120 - Q_PROPERTY(bool negSamples READ get_negSamples WRITE set_negSamples RESET reset_negSamples STORED false)  
121 BR_PROPERTY(br::Transform *, transform, NULL) 191 BR_PROPERTY(br::Transform *, transform, NULL)
122 BR_PROPERTY(double, scaleFactor, 0.75) 192 BR_PROPERTY(double, scaleFactor, 0.75)
123 BR_PROPERTY(bool, takeLargestScale, false) 193 BR_PROPERTY(bool, takeLargestScale, false)
@@ -126,92 +196,27 @@ class BuildScalesTransform : public Transform @@ -126,92 +196,27 @@ class BuildScalesTransform : public Transform
126 BR_PROPERTY(int, minSize, 8) 196 BR_PROPERTY(int, minSize, 8)
127 BR_PROPERTY(double, maxOverlap, 0) 197 BR_PROPERTY(double, maxOverlap, 0)
128 BR_PROPERTY(float, minScale, 1.0) 198 BR_PROPERTY(float, minScale, 1.0)
129 - BR_PROPERTY(bool, negSamples, true)  
130 199
131 -public:  
132 - BuildScalesTransform() : Transform(false, true) {}  
133 private: 200 private:
134 - int windowHeight;  
135 float aspectRatio; 201 float aspectRatio;
  202 + int windowHeight;
136 203
137 - void train(const TemplateList &_data) 204 + void train(const TemplateList &data)
138 { 205 {
139 - TemplateList data(_data); // have to make a copy b/c data is const  
140 aspectRatio = getAspectRatio(data); 206 aspectRatio = getAspectRatio(data);
141 - data.first().file.set("aspectRatio", aspectRatio);  
142 - windowHeight = (int) qRound((float) windowWidth / aspectRatio);  
143 - 207 + windowHeight = qRound(windowWidth / aspectRatio);
144 if (transform->trainable) { 208 if (transform->trainable) {
145 TemplateList full; 209 TemplateList full;
146 - foreach (const Template &tmpl, data) {  
147 - QList<Rect> posRects = OpenCVUtils::toRects(tmpl.file.rects());  
148 - QList<Rect> negRects;  
149 - foreach (Rect posRect, posRects) {  
150 -  
151 - //Adjust for training samples that have different aspect ratios  
152 - int diff = posRect.width - (int)((float) posRect.height * aspectRatio);  
153 - posRect.x += diff / 2;  
154 - posRect.width += diff;  
155 -  
156 - if (posRect.x + posRect.width >= tmpl.m().cols || posRect.y + posRect.height >= tmpl.m().rows || posRect.x < 0 || posRect.y < 0) {  
157 - continue;  
158 - }  
159 -  
160 - Mat scaledImg;  
161 - resize(Mat(tmpl, posRect), scaledImg, Size(windowWidth,qRound(windowWidth / aspectRatio)));  
162 - Template pos(tmpl.file, scaledImg);  
163 - full += pos;  
164 -  
165 - // add random negative samples  
166 - if (negSamples) {  
167 - Mat m = tmpl.m();  
168 - int sample = 0;  
169 - while (sample < negToPosRatio) {  
170 - int x = Common::RandSample(1, m.cols)[0];  
171 - int y = Common::RandSample(1, m.rows)[0];  
172 - int maxWidth = m.cols - x;  
173 - int maxHeight = m.rows - y;  
174 - if (maxWidth <= minSize || maxHeight <= minSize)  
175 - continue;  
176 - int height;  
177 - int width;  
178 - if (aspectRatio > (float) maxWidth / (float) maxHeight) {  
179 - width = Common::RandSample(1,maxWidth,minSize)[0];  
180 - height = (int) qRound(width / aspectRatio);  
181 - } else {  
182 - height = Common::RandSample(1,maxHeight,minSize)[0];  
183 - width = (int) qRound(height * aspectRatio);  
184 - }  
185 - Rect negRect(x, y, width, height);  
186 - // the negative samples cannot overlap the positive at all  
187 - // but they may overlap with other negatives  
188 - if (overlaps(posRects, negRect, 0) || overlaps(negRects, negRect, maxOverlap))  
189 - continue;  
190 - negRects.append(negRect);  
191 - Template neg(tmpl.file, Mat());  
192 - resize(Mat(tmpl, negRect), neg, Size(windowWidth, windowHeight));  
193 - neg.file.set("Label", QString("neg"));  
194 - full += neg;  
195 - sample++;  
196 - }  
197 - }  
198 - } 210 + foreach (const Template &roi, cropTrainingSamples(data, aspectRatio, minSize, maxOverlap, negToPosRatio)) {
  211 + Mat resized;
  212 + resize(roi, resized, Size(windowWidth, windowHeight));
  213 + full += Template(roi.file, resized);
199 } 214 }
  215 + full.first().file.set("aspectRatio", aspectRatio);
200 transform->train(full); 216 transform->train(full);
201 } 217 }
202 } 218 }
203 219
204 - bool overlaps(QList<Rect> posRects, Rect negRect, double overlap)  
205 - {  
206 - foreach (const Rect posRect, posRects) {  
207 - Rect intersect = negRect & posRect;  
208 - if (intersect.area() > overlap*posRect.area())  
209 - return true;  
210 - }  
211 - return false;  
212 - }  
213 -  
214 -  
215 void project(const Template &src, Template &dst) const 220 void project(const Template &src, Template &dst) const
216 { 221 {
217 dst = src; 222 dst = src;
@@ -235,12 +240,47 @@ private: @@ -235,12 +240,47 @@ private:
235 return; 240 return;
236 } 241 }
237 } 242 }
  243 +
  244 + void store(QDataStream &stream) const
  245 + {
  246 + transform->store(stream);
  247 + stream << aspectRatio << windowHeight;
  248 + }
  249 + void load(QDataStream &stream)
  250 + {
  251 + transform->load(stream);
  252 + stream >> aspectRatio >> windowHeight;
  253 + }
238 }; 254 };
239 255
240 BR_REGISTER(Transform, BuildScalesTransform) 256 BR_REGISTER(Transform, BuildScalesTransform)
241 257
242 /*! 258 /*!
243 * \ingroup transforms 259 * \ingroup transforms
  260 + * \brief Sample detection bounding boxes from integral images
  261 + * \author Josh Klontz \cite jklontz
  262 + */
  263 +class IntegralDetector : public Transform
  264 +{
  265 + Q_OBJECT
  266 + Q_PROPERTY(br::Transform *transform READ get_transform WRITE set_transform RESET reset_transform)
  267 + BR_PROPERTY(br::Transform*, transform, make("Identity"))
  268 +
  269 + void train(const TemplateList &data)
  270 + {
  271 + transform->train(cropTrainingSamples(data, getAspectRatio(data)));
  272 + }
  273 +
  274 + void project(const Template &src, Template &dst) const
  275 + {
  276 + dst = src;
  277 + }
  278 +};
  279 +
  280 +BR_REGISTER(Transform, IntegralDetector)
  281 +
  282 +/*!
  283 + * \ingroup transforms
244 * \brief Detects objects with OpenCV's built-in HOG detection. 284 * \brief Detects objects with OpenCV's built-in HOG detection.
245 * \author Austin Blanton \cite imaus10 285 * \author Austin Blanton \cite imaus10
246 */ 286 */
@@ -291,6 +331,8 @@ private: @@ -291,6 +331,8 @@ private:
291 //Compute overlap between rectangles and create discrete Laplacian matrix 331 //Compute overlap between rectangles and create discrete Laplacian matrix
292 QList<Rect> rects = OpenCVUtils::toRects(src.file.rects()); 332 QList<Rect> rects = OpenCVUtils::toRects(src.file.rects());
293 int n = rects.size(); 333 int n = rects.size();
  334 + if (n == 0)
  335 + return;
294 MatrixXf laplace(n,n); 336 MatrixXf laplace(n,n);
295 for (int i = 0; i < n; i++) { 337 for (int i = 0; i < n; i++) {
296 laplace(i,i) = 0; 338 laplace(i,i) = 0;
openbr/plugins/stream.cpp
@@ -16,6 +16,17 @@ using namespace cv; @@ -16,6 +16,17 @@ using namespace cv;
16 namespace br 16 namespace br
17 { 17 {
18 18
  19 +class Idiocy : public QObject
  20 +{
  21 + Q_OBJECT
  22 +public:
  23 + enum StreamModes { StreamVideo,
  24 + DistributeFrames,
  25 + Auto};
  26 +
  27 + Q_ENUMS(StreamModes)
  28 +};
  29 +
19 class FrameData 30 class FrameData
20 { 31 {
21 public: 32 public:
@@ -180,198 +191,29 @@ private: @@ -180,198 +191,29 @@ private:
180 QList<FrameData *> buffer2; 191 QList<FrameData *> buffer2;
181 }; 192 };
182 193
183 -  
184 -// Interface for sequentially getting data from some data source.  
185 -// Initialized off of a template, can represent a video file (stored in the template's filename)  
186 -// or a set of images already loaded into memory stored as multiple matrices in an input template.  
187 -class DataSource 194 +// Given a template as input, return N templates as output, one at a time on subsequent
  195 +// calls to getNext
  196 +class TemplateProcessor
188 { 197 {
189 public: 198 public:
190 - DataSource(int maxFrames=500)  
191 - {  
192 - // The sequence number of the last frame  
193 - final_frame = -1;  
194 - for (int i=0; i < maxFrames;i++)  
195 - {  
196 - allFrames.addItem(new FrameData());  
197 - }  
198 - }  
199 -  
200 - virtual ~DataSource()  
201 - {  
202 - while (true)  
203 - {  
204 - FrameData * frame = allFrames.tryGetItem();  
205 - if (frame == NULL)  
206 - break;  
207 - delete frame;  
208 - }  
209 - }  
210 -  
211 - // non-blocking version of getFrame  
212 - // Returns a NULL FrameData if too many frames are out, or the  
213 - // data source is broken. Sets last_frame to true iff the FrameData  
214 - // returned is the last valid frame, and the data source is now broken.  
215 - FrameData * tryGetFrame(bool & last_frame)  
216 - {  
217 - last_frame = false;  
218 -  
219 - if (is_broken) {  
220 - return NULL;  
221 - }  
222 -  
223 - // Try to get a FrameData from the pool, if we can't it means too many  
224 - // frames are already out, and we will return NULL to indicate failure  
225 - FrameData * aFrame = allFrames.tryGetItem();  
226 - if (aFrame == NULL)  
227 - return NULL;  
228 -  
229 - // Try to actually read a frame, if this returns false the data source is broken  
230 - bool res = getNext(*aFrame);  
231 -  
232 - // The datasource broke, update final_frame  
233 - if (!res)  
234 - {  
235 - QMutexLocker lock(&last_frame_update);  
236 - final_frame = lookAhead.back()->sequenceNumber;  
237 - allFrames.addItem(aFrame);  
238 - }  
239 - else {  
240 - lookAhead.push_back(aFrame);  
241 - }  
242 -  
243 - // we will return the first frame on the lookAhead buffer  
244 - FrameData * rVal = lookAhead.first();  
245 - lookAhead.pop_front();  
246 -  
247 - // If this is the last frame, say so  
248 - if (rVal->sequenceNumber == final_frame) {  
249 - last_frame = true;  
250 - is_broken = true;  
251 - }  
252 -  
253 - return rVal;  
254 - }  
255 -  
256 - // Return a frame to the pool, returns true if the frame returned was the last  
257 - // frame issued, false otherwise  
258 - bool returnFrame(FrameData * inputFrame)  
259 - {  
260 - int frameNumber = inputFrame->sequenceNumber;  
261 -  
262 - inputFrame->data.clear();  
263 - inputFrame->sequenceNumber = -1;  
264 - allFrames.addItem(inputFrame);  
265 -  
266 - bool rval = false;  
267 -  
268 - QMutexLocker lock(&last_frame_update);  
269 -  
270 - if (frameNumber == final_frame) {  
271 - // We just received the last frame, better pulse  
272 - allReturned = true;  
273 - lastReturned.wakeAll();  
274 - rval = true;  
275 - }  
276 -  
277 - return rval;  
278 - }  
279 -  
280 - bool waitLast()  
281 - {  
282 - QMutexLocker lock(&last_frame_update);  
283 -  
284 - while (!allReturned)  
285 - {  
286 - // This would be a safer wait if we used a timeout, but  
287 - // theoretically that should never matter.  
288 - lastReturned.wait(&last_frame_update);  
289 - }  
290 - return true;  
291 - }  
292 -  
293 - bool open(Template & output, int start_index = 0)  
294 - {  
295 - is_broken = false;  
296 - allReturned = false;  
297 -  
298 - // The last frame isn't initialized yet  
299 - final_frame = -1;  
300 - // Start our sequence numbers from the input index  
301 - next_sequence_number = start_index;  
302 -  
303 - // Actually open the data source  
304 - bool open_res = concreteOpen(output);  
305 -  
306 - // We couldn't open the data source  
307 - if (!open_res) {  
308 - is_broken = true;  
309 - return false;  
310 - }  
311 -  
312 - // Try to get a frame from the global pool  
313 - FrameData * firstFrame = allFrames.tryGetItem();  
314 -  
315 - // If this fails, things have gone pretty badly.  
316 - if (firstFrame == NULL) {  
317 - is_broken = true;  
318 - return false;  
319 - }  
320 -  
321 - // Read a frame from the video source  
322 - bool res = getNext(*firstFrame);  
323 -  
324 - // the data source broke already, we couldn't even get one frame  
325 - // from it even though it claimed to have opened successfully.  
326 - if (!res) {  
327 - is_broken = true;  
328 - return false;  
329 - }  
330 -  
331 - // We read one frame ahead of the last one returned, this allows  
332 - // us to know which frame is the final frame when we return it.  
333 - lookAhead.append(firstFrame);  
334 - return true;  
335 - }  
336 -  
337 - /*  
338 - * Pure virtual methods  
339 - */  
340 -  
341 - // isOpen doesn't appear to particularly work when used on opencv  
342 - // VideoCaptures, so we don't use it for anything important. 199 + virtual ~TemplateProcessor() {}
  200 + virtual bool open(Template & input)=0;
343 virtual bool isOpen()=0; 201 virtual bool isOpen()=0;
344 - // Called from open, open the data source specified by the input  
345 - // template, don't worry about setting any of the state variables  
346 - // set in open.  
347 - virtual bool concreteOpen(Template & output) = 0;  
348 - // Get the next frame from the data source, store the results in  
349 - // FrameData (including the actual frame and appropriate sequence  
350 - // number).  
351 - virtual bool getNext(FrameData & input) = 0;  
352 - // close the currently open data source.  
353 - virtual void close() = 0;  
354 -  
355 - int next_sequence_number; 202 + virtual void close()=0;
  203 + virtual bool getNextTemplate(Template & output)=0;
356 protected: 204 protected:
357 - DoubleBuffer allFrames;  
358 - int final_frame;  
359 - bool is_broken;  
360 - bool allReturned;  
361 - QList<FrameData *> lookAhead;  
362 -  
363 - QWaitCondition lastReturned;  
364 - QMutex last_frame_update; 205 + Template basis;
365 }; 206 };
366 207
367 static QMutex openLock; 208 static QMutex openLock;
  209 +
368 // Read a video frame by frame using cv::VideoCapture 210 // Read a video frame by frame using cv::VideoCapture
369 -class VideoDataSource : public DataSource 211 +class VideoReader : public TemplateProcessor
370 { 212 {
371 public: 213 public:
372 - VideoDataSource(int maxFrames) : DataSource(maxFrames) {} 214 + VideoReader() {}
373 215
374 - bool concreteOpen(Template &input) 216 + bool open(Template &input)
375 { 217 {
376 basis = input; 218 basis = input;
377 219
@@ -407,30 +249,23 @@ public: @@ -407,30 +249,23 @@ public:
407 249
408 bool isOpen() { return video.isOpened(); } 250 bool isOpen() { return video.isOpened(); }
409 251
410 - void close() {  
411 - video.release();  
412 - } 252 + void close() { video.release(); }
413 253
414 -private:  
415 - bool getNext(FrameData & output) 254 + bool getNextTemplate(Template & output)
416 { 255 {
417 if (!isOpen()) { 256 if (!isOpen()) {
418 qDebug("video source is not open"); 257 qDebug("video source is not open");
419 return false; 258 return false;
420 } 259 }
421 -  
422 - output.data.append(Template(basis.file));  
423 - output.data.last().m() = cv::Mat();  
424 -  
425 - output.sequenceNumber = next_sequence_number;  
426 - next_sequence_number++; 260 + output.file = basis.file;
  261 + output.m() = cv::Mat();
427 262
428 cv::Mat temp; 263 cv::Mat temp;
429 bool res = video.read(temp); 264 bool res = video.read(temp);
430 265
431 if (!res) { 266 if (!res) {
432 // The video capture broke, return false. 267 // The video capture broke, return false.
433 - output.data.last().m() = cv::Mat(); 268 + output.m() = cv::Mat();
434 close(); 269 close();
435 return false; 270 return false;
436 } 271 }
@@ -438,198 +273,330 @@ private: @@ -438,198 +273,330 @@ private:
438 // This clone is critical, if we don't do it then the matrix will 273 // This clone is critical, if we don't do it then the matrix will
439 // be an alias of an internal buffer of the video source, leading 274 // be an alias of an internal buffer of the video source, leading
440 // to various problems later. 275 // to various problems later.
441 - output.data.last().m() = temp.clone();  
442 -  
443 - output.data.last().file.set("FrameNumber", output.sequenceNumber); 276 + output.m() = temp.clone();
444 return true; 277 return true;
445 } 278 }
446 - 279 +protected:
447 cv::VideoCapture video; 280 cv::VideoCapture video;
448 - Template basis;  
449 }; 281 };
450 282
451 -// Given a template as input, return its matrices one by one on subsequent calls  
452 -// to getNext  
453 -class TemplateDataSource : public DataSource 283 +
  284 +class DirectReturn : public TemplateProcessor
454 { 285 {
455 public: 286 public:
456 - TemplateDataSource(int maxFrames) : DataSource(maxFrames) 287 + DirectReturn()
457 { 288 {
458 - current_matrix_idx = INT_MAX;  
459 data_ok = false; 289 data_ok = false;
460 } 290 }
461 291
462 - // To "open" it we just set appropriate indices, we assume that if this  
463 - // is an image, it is already loaded into memory.  
464 - bool concreteOpen(Template &input) 292 + // We don't do anything, just prepare to return input when getNext is called.
  293 + bool open(Template &input)
465 { 294 {
466 basis = input; 295 basis = input;
467 - current_matrix_idx = 0;  
468 -  
469 - data_ok = current_matrix_idx < basis.size(); 296 + data_ok =true;
470 return data_ok; 297 return data_ok;
471 } 298 }
472 299
473 - bool isOpen() {  
474 - return data_ok;  
475 - } 300 + bool isOpen() { return data_ok; }
476 301
477 void close() 302 void close()
478 { 303 {
479 - current_matrix_idx = INT_MAX; 304 + data_ok = false;
480 basis.clear(); 305 basis.clear();
481 } 306 }
482 307
483 -private:  
484 - bool getNext(FrameData & output) 308 + bool getNextTemplate(Template & output)
485 { 309 {
486 - data_ok = current_matrix_idx < basis.size();  
487 if (!data_ok) 310 if (!data_ok)
488 return false; 311 return false;
489 -  
490 - output.data.append(basis[current_matrix_idx]);  
491 - current_matrix_idx++;  
492 -  
493 - output.sequenceNumber = next_sequence_number;  
494 - next_sequence_number++;  
495 -  
496 - output.data.last().file.set("FrameNumber", output.sequenceNumber); 312 + output = basis;
  313 + data_ok = false;
497 return true; 314 return true;
498 } 315 }
499 316
500 - Template basis;  
501 - // Index of the next matrix to output from the template  
502 - int current_matrix_idx;  
503 -  
504 - // is current_matrix_idx in bounds? 317 +protected:
  318 + // Have we sent our template yet?
505 bool data_ok; 319 bool data_ok;
506 }; 320 };
507 321
508 -// Given a templatelist as input, create appropriate data source for each  
509 -// individual template  
510 -class DataSourceManager : public DataSource 322 +
  323 +// Interface for sequentially getting data from some data source.
  324 +// Given a TemplateList, return single template frames sequentially by applying a TemplateProcessor
  325 +// to each individual template.
  326 +class DataSource
511 { 327 {
512 public: 328 public:
513 - DataSourceManager(int activeFrames=100) : DataSource(activeFrames) 329 + DataSource(int maxFrames=500)
514 { 330 {
515 - actualSource = NULL; 331 + // The sequence number of the last frame
  332 + final_frame = -1;
  333 + for (int i=0; i < maxFrames;i++)
  334 + {
  335 + allFrames.addItem(new FrameData());
  336 + }
  337 + frameSource = NULL;
516 } 338 }
517 339
518 - ~DataSourceManager() 340 + virtual ~DataSource()
519 { 341 {
520 - close(); 342 + while (true)
  343 + {
  344 + FrameData * frame = allFrames.tryGetItem();
  345 + if (frame == NULL)
  346 + break;
  347 + delete frame;
  348 + }
521 } 349 }
522 350
523 - int size() 351 + void close()
524 { 352 {
525 - return this->allFrames.size(); 353 + if (this->frameSource)
  354 + {
  355 + frameSource->close();
  356 + delete frameSource;
  357 + frameSource = NULL;
  358 + }
526 } 359 }
527 360
528 - void close() 361 + int size()
529 { 362 {
530 - if (actualSource) {  
531 - actualSource->close();  
532 - delete actualSource;  
533 - actualSource = NULL;  
534 - } 363 + return this->templates.size();
535 } 364 }
536 365
537 - // We are used through a call to open(TemplateList)  
538 - bool open(TemplateList & input) 366 + bool open(TemplateList & input, br::Idiocy::StreamModes _mode)
539 { 367 {
540 // Set up variables specific to us 368 // Set up variables specific to us
541 current_template_idx = 0; 369 current_template_idx = 0;
542 templates = input; 370 templates = input;
  371 + mode = _mode;
  372 +
  373 + is_broken = false;
  374 + allReturned = false;
543 375
544 - // Call datasourece::open on the first template to set up  
545 - // state variables  
546 - return DataSource::open(templates[current_template_idx]); 376 + // The last frame isn't initialized yet
  377 + final_frame = -1;
  378 + // Start our sequence numbers from the input index
  379 + next_sequence_number = 0;
  380 +
  381 + // Actually open the data source
  382 + bool open_res = openNextTemplate();
  383 +
  384 + // We couldn't open the data source
  385 + if (!open_res) {
  386 + is_broken = true;
  387 + return false;
  388 + }
  389 +
  390 + // Try to get a frame from the global pool
  391 + FrameData * firstFrame = allFrames.tryGetItem();
  392 +
  393 + // If this fails, things have gone pretty badly.
  394 + if (firstFrame == NULL) {
  395 + is_broken = true;
  396 + return false;
  397 + }
  398 +
  399 + // Read a frame from the video source
  400 + bool res = getNextFrame(*firstFrame);
  401 +
  402 + // the data source broke already, we couldn't even get one frame
  403 + // from it even though it claimed to have opened successfully.
  404 + if (!res) {
  405 + is_broken = true;
  406 + return false;
  407 + }
  408 +
  409 + // We read one frame ahead of the last one returned, this allows
  410 + // us to know which frame is the final frame when we return it.
  411 + lookAhead.append(firstFrame);
  412 + return true;
547 } 413 }
548 414
549 - // Create an actual data source of appropriate type for this template  
550 - // (initially called via the call to DataSource::open, called later  
551 - // as we run out of frames on our templates).  
552 - bool concreteOpen(Template & input) 415 +
  416 + // non-blocking version of getFrame
  417 + // Returns a NULL FrameData if too many frames are out, or the
  418 + // data source is broken. Sets last_frame to true iff the FrameData
  419 + // returned is the last valid frame, and the data source is now broken.
  420 + FrameData * tryGetFrame(bool & last_frame)
553 { 421 {
554 - close(); 422 + last_frame = false;
555 423
556 - bool open_res = false;  
557 - // Input has no matrices? Its probably a video that hasn't been loaded yet  
558 - if (input.empty()) {  
559 - actualSource = new VideoDataSource(0);  
560 - open_res = actualSource->concreteOpen(input); 424 + if (is_broken) {
  425 + return NULL;
  426 + }
  427 +
  428 + // Try to get a FrameData from the pool, if we can't it means too many
  429 + // frames are already out, and we will return NULL to indicate failure
  430 + FrameData * aFrame = allFrames.tryGetItem();
  431 + if (aFrame == NULL)
  432 + return NULL;
  433 +
  434 + // Try to actually read a frame, if this returns false the data source is broken
  435 + bool res = getNextFrame(*aFrame);
  436 +
  437 + // The datasource broke, update final_frame
  438 + if (!res)
  439 + {
  440 + QMutexLocker lock(&last_frame_update);
  441 + final_frame = lookAhead.back()->sequenceNumber;
  442 + allFrames.addItem(aFrame);
561 } 443 }
562 - // If the input is not empty, we assume it is a set of frames already  
563 - // in memory.  
564 else { 444 else {
565 - actualSource = new TemplateDataSource(0);  
566 - open_res = actualSource->concreteOpen(input); 445 + lookAhead.push_back(aFrame);
567 } 446 }
568 447
569 - // The data source failed to open  
570 - if (!open_res) {  
571 - delete actualSource;  
572 - actualSource = NULL;  
573 - return false; 448 + // we will return the first frame on the lookAhead buffer
  449 + FrameData * rVal = lookAhead.first();
  450 + lookAhead.pop_front();
  451 + if (rVal->data.empty())
  452 + qDebug("returning empty frame from look ahead!");
  453 +
  454 + // If this is the last frame, say so
  455 + if (rVal->sequenceNumber == final_frame) {
  456 + last_frame = true;
  457 + is_broken = true;
574 } 458 }
575 - return true; 459 +
  460 + return rVal;
576 } 461 }
577 462
578 - bool isOpen() { return !actualSource ? false : actualSource->isOpen(); } 463 + // Return a frame to the pool, returns true if the frame returned was the last
  464 + // frame issued, false otherwise
  465 + bool returnFrame(FrameData * inputFrame)
  466 + {
  467 + int frameNumber = inputFrame->sequenceNumber;
579 468
580 -protected:  
581 - // Index of the template in the templatelist we are currently reading from  
582 - int current_template_idx; 469 + inputFrame->data.clear();
  470 + inputFrame->sequenceNumber = -1;
  471 + allFrames.addItem(inputFrame);
583 472
584 - TemplateList templates;  
585 - DataSource * actualSource;  
586 - // Get the next frame, if we run out of frames on the current template  
587 - // move on to the next one.  
588 - bool getNext(FrameData & output) 473 + bool rval = false;
  474 +
  475 + QMutexLocker lock(&last_frame_update);
  476 +
  477 + if (frameNumber == final_frame) {
  478 + // We just received the last frame, better pulse
  479 + allReturned = true;
  480 + lastReturned.wakeAll();
  481 + rval = true;
  482 + }
  483 +
  484 + return rval;
  485 + }
  486 +
  487 + bool waitLast()
589 { 488 {
590 - bool res = actualSource->getNext(output);  
591 - output.sequenceNumber = next_sequence_number;  
592 -  
593 - // OK we got a frame  
594 - if (res) {  
595 - // Override the sequence number set by actualSource  
596 - output.data.last().file.set("FrameNumber", output.sequenceNumber);  
597 - next_sequence_number++;  
598 - if (output.data.last().last().empty())  
599 - qDebug("broken matrix");  
600 - return true; 489 + QMutexLocker lock(&last_frame_update);
  490 +
  491 + while (!allReturned)
  492 + {
  493 + // This would be a safer wait if we used a timeout, but
  494 + // theoretically that should never matter.
  495 + lastReturned.wait(&last_frame_update);
601 } 496 }
  497 + return true;
  498 + }
602 499
603 - // We didn't get a frame, try to move on to the next template.  
604 - while(!res) {  
605 - output.data.clear();  
606 - current_template_idx++; 500 +protected:
607 501
608 - // No more templates? We're done  
609 - if (current_template_idx >= templates.size())  
610 - return false; 502 + bool openNextTemplate()
  503 + {
  504 + if (this->current_template_idx >= this->templates.size())
  505 + return false;
611 506
612 - // open the next data source  
613 - bool open_res = concreteOpen(templates[current_template_idx]);  
614 - // We couldn't open it, give up? We could maybe continue here  
615 - // but don't currently.  
616 - if (!open_res)  
617 - return false; 507 + bool open_res = false;
  508 + while (!open_res)
  509 + {
  510 + if (frameSource)
  511 + frameSource->close();
618 512
619 - // get a frame from the newly opened data source, if that fails  
620 - // we continue to open the next one.  
621 - res = actualSource->getNext(output); 513 + if (mode == br::Idiocy::Auto)
  514 + {
  515 + delete frameSource;
  516 + if (this->templates[this->current_template_idx].empty())
  517 + frameSource = new VideoReader();
  518 + else
  519 + frameSource = new DirectReturn();
  520 + }
  521 + else if (mode == br::Idiocy::DistributeFrames)
  522 + {
  523 + if (!frameSource)
  524 + frameSource = new DirectReturn();
  525 + }
  526 + else if (mode == br::Idiocy::StreamVideo)
  527 + {
  528 + if (!frameSource)
  529 + frameSource = new VideoReader();
  530 + }
  531 +
  532 + open_res = frameSource->open(this->templates[current_template_idx]);
  533 + if (!open_res)
  534 + {
  535 + current_template_idx++;
  536 + if (current_template_idx >= this->templates.size())
  537 + return false;
  538 + }
622 } 539 }
623 - // Finally, set the sequence number for the frame we actually return.  
624 - output.sequenceNumber = next_sequence_number++;  
625 - output.data.last().file.set("FrameNumber", output.sequenceNumber); 540 + return true;
  541 + }
626 542
627 - if (output.data.last().last().empty())  
628 - qDebug("broken matrix"); 543 + bool getNextFrame(FrameData & output)
  544 + {
  545 + bool got_frame = false;
629 546
630 - return res; 547 + Template aTemplate;
  548 +
  549 + while (!got_frame)
  550 + {
  551 + got_frame = frameSource->getNextTemplate(aTemplate);
  552 +
  553 + // OK we got a frame
  554 + if (got_frame) {
  555 + // set the sequence number and tempalte of this frame
  556 + output.sequenceNumber = next_sequence_number;
  557 + output.data.append(aTemplate);
  558 + // set the frame number in the template's metadata
  559 + output.data.last().file.set("FrameNumber", output.sequenceNumber);
  560 + next_sequence_number++;
  561 + return true;
  562 + }
  563 +
  564 + // advance to the next tempalte in our list
  565 + this->current_template_idx++;
  566 + bool open_res = this->openNextTemplate();
  567 +
  568 + // couldn't get the next template? nothing to do, otherwise we try to read
  569 + // a frame at the top of this loop.
  570 + if (!open_res) {
  571 + return false;
  572 + }
  573 + }
  574 +
  575 + return false;
631 } 576 }
632 577
  578 + // Index of the template in the templatelist we are currently reading from
  579 + int current_template_idx;
  580 +
  581 + // What do we do to each template
  582 + br::Idiocy::StreamModes mode;
  583 +
  584 + // list of templates we are workign from
  585 + TemplateList templates;
  586 +
  587 + // processor for the current template
  588 + TemplateProcessor * frameSource;
  589 +
  590 + int next_sequence_number;
  591 + int final_frame;
  592 + bool is_broken;
  593 + bool allReturned;
  594 +
  595 + DoubleBuffer allFrames;
  596 + QList<FrameData *> lookAhead;
  597 +
  598 + QWaitCondition lastReturned;
  599 + QMutex last_frame_update;
633 }; 600 };
634 601
635 class ProcessingStage; 602 class ProcessingStage;
@@ -710,6 +677,7 @@ public: @@ -710,6 +677,7 @@ public:
710 if (input == NULL) { 677 if (input == NULL) {
711 qFatal("null input to multi-thread stage"); 678 qFatal("null input to multi-thread stage");
712 } 679 }
  680 +
713 input->data >> *transform; 681 input->data >> *transform;
714 682
715 should_continue = nextStage->tryAcquireNextStage(input); 683 should_continue = nextStage->tryAcquireNextStage(input);
@@ -861,13 +829,35 @@ public: @@ -861,13 +829,35 @@ public:
861 829
862 }; 830 };
863 831
  832 +// Semi-functional, doesn't do anything productive outside of stream::train
  833 +class CollectSets : public TimeVaryingTransform
  834 +{
  835 + Q_OBJECT
  836 +public:
  837 + CollectSets() : TimeVaryingTransform(false, false) {}
  838 +
  839 + QList<TemplateList> sets;
  840 +
  841 + void projectUpdate(const TemplateList &src, TemplateList &dst)
  842 + {
  843 + (void) dst;
  844 + sets.append(src);
  845 + }
  846 +
  847 + void train(const TemplateList & data)
  848 + {
  849 + (void) data;
  850 + }
  851 +
  852 +};
  853 +
864 // This stage reads new frames from the data source. 854 // This stage reads new frames from the data source.
865 -class FirstStage : public SingleThreadStage 855 +class ReadStage : public SingleThreadStage
866 { 856 {
867 public: 857 public:
868 - FirstStage(int activeFrames = 100) : SingleThreadStage(true), dataSource(activeFrames){ } 858 + ReadStage(int activeFrames = 100) : SingleThreadStage(true), dataSource(activeFrames){ }
869 859
870 - DataSourceManager dataSource; 860 + DataSource dataSource;
871 861
872 void reset() 862 void reset()
873 { 863 {
@@ -951,99 +941,17 @@ public: @@ -951,99 +941,17 @@ public:
951 void status(){ 941 void status(){
952 qDebug("Read stage %d, status starting? %d, next frame %d buffer size %d", this->stage_id, this->currentStatus == SingleThreadStage::STARTING, this->next_target, this->dataSource.size()); 942 qDebug("Read stage %d, status starting? %d, next frame %d buffer size %d", this->stage_id, this->currentStatus == SingleThreadStage::STARTING, this->next_target, this->dataSource.size());
953 } 943 }
954 -  
955 -  
956 -};  
957 -  
958 -// Appened to the end of a Stream's transform sequence. Collects the output  
959 -// from each frame on a single templatelist  
960 -class LastStage : public SingleThreadStage  
961 -{  
962 -public:  
963 - LastStage(bool _prev_stage_variance) : SingleThreadStage(_prev_stage_variance) {}  
964 - TemplateList getOutput()  
965 - {  
966 - return collectedOutput;  
967 - }  
968 -  
969 -private:  
970 - TemplateList collectedOutput;  
971 -public:  
972 -  
973 - void reset()  
974 - {  
975 - collectedOutput.clear();  
976 - SingleThreadStage::reset();  
977 - }  
978 -  
979 - FrameData * run(FrameData * input, bool & should_continue)  
980 - {  
981 - if (input == NULL) {  
982 - qFatal("NULL input to stage %d", this->stage_id);  
983 - }  
984 -  
985 - if (input->sequenceNumber != next_target)  
986 - {  
987 - qFatal("out of order frames for stage %d, got %d expected %d", this->stage_id, input->sequenceNumber, this->next_target);  
988 - }  
989 - next_target = input->sequenceNumber + 1;  
990 -  
991 - // add the item to our output buffer  
992 - collectedOutput.append(input->data);  
993 -  
994 - // Can we enter the read stage?  
995 - should_continue = nextStage->tryAcquireNextStage(input);  
996 -  
997 - // Is there anything on our input buffer? If so we should start a thread  
998 - // in this stage to process that frame.  
999 - QWriteLocker lock(&statusLock);  
1000 - FrameData * newItem = inputBuffer->tryGetItem();  
1001 - if (!newItem)  
1002 - {  
1003 - this->currentStatus = STOPPING;  
1004 - }  
1005 - lock.unlock();  
1006 -  
1007 - if (newItem)  
1008 - startThread(newItem);  
1009 -  
1010 - return input;  
1011 - }  
1012 -  
1013 - void status(){  
1014 - qDebug("Collection stage %d, status starting? %d, next %d buffer size %d", this->stage_id, this->currentStatus == SingleThreadStage::STARTING, this->next_target, this->inputBuffer->size());  
1015 - }  
1016 -  
1017 -};  
1018 -  
1019 -// Semi-functional, doesn't do anything productive outside of stream::train  
1020 -class CollectSets : public TimeVaryingTransform  
1021 -{  
1022 - Q_OBJECT  
1023 -public:  
1024 - CollectSets() : TimeVaryingTransform(false, false) {}  
1025 -  
1026 - QList<TemplateList> sets;  
1027 -  
1028 - void projectUpdate(const TemplateList &src, TemplateList &dst)  
1029 - {  
1030 - (void) dst;  
1031 - sets.append(src);  
1032 - }  
1033 -  
1034 - void train(const TemplateList & data)  
1035 - {  
1036 - (void) data;  
1037 - }  
1038 -  
1039 }; 944 };
1040 945
1041 class DirectStreamTransform : public CompositeTransform 946 class DirectStreamTransform : public CompositeTransform
1042 { 947 {
1043 Q_OBJECT 948 Q_OBJECT
1044 public: 949 public:
  950 +
1045 Q_PROPERTY(int activeFrames READ get_activeFrames WRITE set_activeFrames RESET reset_activeFrames) 951 Q_PROPERTY(int activeFrames READ get_activeFrames WRITE set_activeFrames RESET reset_activeFrames)
  952 + Q_PROPERTY(br::Idiocy::StreamModes readMode READ get_readMode WRITE set_readMode RESET reset_readMode)
1046 BR_PROPERTY(int, activeFrames, 100) 953 BR_PROPERTY(int, activeFrames, 100)
  954 + BR_PROPERTY(br::Idiocy::StreamModes, readMode, br::Idiocy::Auto)
1047 955
1048 friend class StreamTransfrom; 956 friend class StreamTransfrom;
1049 957
@@ -1109,13 +1017,21 @@ public: @@ -1109,13 +1017,21 @@ public:
1109 qFatal("whatever"); 1017 qFatal("whatever");
1110 } 1018 }
1111 1019
  1020 +
  1021 + virtual void finalize(TemplateList & output)
  1022 + {
  1023 + (void) output;
  1024 + // Nothing in particular to do here, stream calls finalize
  1025 + // on all child transforms as part of projectUpdate
  1026 + }
  1027 +
1112 // start processing, consider all templates in src a continuous 1028 // start processing, consider all templates in src a continuous
1113 // 'video' 1029 // 'video'
1114 void projectUpdate(const TemplateList & src, TemplateList & dst) 1030 void projectUpdate(const TemplateList & src, TemplateList & dst)
1115 { 1031 {
1116 dst = src; 1032 dst = src;
1117 1033
1118 - bool res = readStage->dataSource.open(dst); 1034 + bool res = readStage->dataSource.open(dst,readMode);
1119 if (!res) { 1035 if (!res) {
1120 qDebug("stream failed to open %s", qPrintable(dst[0].file.name)); 1036 qDebug("stream failed to open %s", qPrintable(dst[0].file.name));
1121 return; 1037 return;
@@ -1149,6 +1065,8 @@ public: @@ -1149,6 +1065,8 @@ public:
1149 { 1065 {
1150 TemplateList output_set; 1066 TemplateList output_set;
1151 transforms[i]->finalize(output_set); 1067 transforms[i]->finalize(output_set);
  1068 + if (output_set.empty())
  1069 + continue;
1152 1070
1153 for (int j=i+1; j < transforms.size();j++) 1071 for (int j=i+1; j < transforms.size();j++)
1154 { 1072 {
@@ -1159,7 +1077,12 @@ public: @@ -1159,7 +1077,12 @@ public:
1159 1077
1160 // dst is set to all output received by the final stage, along 1078 // dst is set to all output received by the final stage, along
1161 // with anything output via the calls to finalize. 1079 // with anything output via the calls to finalize.
1162 - dst = collectionStage->getOutput(); 1080 + //dst = collectionStage->getOutput();
  1081 + foreach(const TemplateList & list, collector->sets) {
  1082 + dst.append(list);
  1083 + }
  1084 + collector->sets.clear();
  1085 +
1163 dst.append(final_output); 1086 dst.append(final_output);
1164 1087
1165 foreach(ProcessingStage * stage, processingStages) { 1088 foreach(ProcessingStage * stage, processingStages) {
@@ -1167,12 +1090,6 @@ public: @@ -1167,12 +1090,6 @@ public:
1167 } 1090 }
1168 } 1091 }
1169 1092
1170 - virtual void finalize(TemplateList & output)  
1171 - {  
1172 - (void) output;  
1173 - // Nothing in particular to do here, stream calls finalize  
1174 - // on all child transforms as part of projectUpdate  
1175 - }  
1176 1093
1177 // Create and link stages 1094 // Create and link stages
1178 void init() 1095 void init()
@@ -1193,7 +1110,7 @@ public: @@ -1193,7 +1110,7 @@ public:
1193 QMutexLocker poolLock(&poolsAccess); 1110 QMutexLocker poolLock(&poolsAccess);
1194 QHash<QObject *, QThreadPool *>::Iterator it; 1111 QHash<QObject *, QThreadPool *>::Iterator it;
1195 if (!pools.contains(this->parent())) { 1112 if (!pools.contains(this->parent())) {
1196 - it = pools.insert(this->parent(), new QThreadPool(this)); 1113 + it = pools.insert(this->parent(), new QThreadPool(this->parent()));
1197 it.value()->setMaxThreadCount(Globals->parallelism); 1114 it.value()->setMaxThreadCount(Globals->parallelism);
1198 } 1115 }
1199 else it = pools.find(this->parent()); 1116 else it = pools.find(this->parent());
@@ -1202,6 +1119,7 @@ public: @@ -1202,6 +1119,7 @@ public:
1202 1119
1203 // Are our children time varying or not? This decides whether 1120 // Are our children time varying or not? This decides whether
1204 // we run them in single threaded or multi threaded stages 1121 // we run them in single threaded or multi threaded stages
  1122 + stage_variance.clear();
1205 stage_variance.reserve(transforms.size()); 1123 stage_variance.reserve(transforms.size());
1206 foreach (const br::Transform *transform, transforms) { 1124 foreach (const br::Transform *transform, transforms) {
1207 stage_variance.append(transform->timeVarying()); 1125 stage_variance.append(transform->timeVarying());
@@ -1209,7 +1127,7 @@ public: @@ -1209,7 +1127,7 @@ public:
1209 1127
1210 // Additionally, we have a separate stage responsible for reading 1128 // Additionally, we have a separate stage responsible for reading
1211 // frames from the data source 1129 // frames from the data source
1212 - readStage = new FirstStage(activeFrames); 1130 + readStage = new ReadStage(activeFrames);
1213 1131
1214 processingStages.push_back(readStage); 1132 processingStages.push_back(readStage);
1215 readStage->stage_id = 0; 1133 readStage->stage_id = 0;
@@ -1244,7 +1162,10 @@ public: @@ -1244,7 +1162,10 @@ public:
1244 1162
1245 // We also have the last stage, which just puts the output of the 1163 // We also have the last stage, which just puts the output of the
1246 // previous stages on a template list. 1164 // previous stages on a template list.
1247 - collectionStage = new LastStage(prev_stage_variance); 1165 + collectionStage = new SingleThreadStage(prev_stage_variance);
  1166 + collectionStage->transform = this->collector.data();
  1167 +
  1168 +
1248 processingStages.append(collectionStage); 1169 processingStages.append(collectionStage);
1249 collectionStage->stage_id = next_stage_id; 1170 collectionStage->stage_id = next_stage_id;
1250 collectionStage->stages = &this->processingStages; 1171 collectionStage->stages = &this->processingStages;
@@ -1258,6 +1179,11 @@ public: @@ -1258,6 +1179,11 @@ public:
1258 collectionStage->nextStage = readStage; 1179 collectionStage->nextStage = readStage;
1259 } 1180 }
1260 1181
  1182 + DirectStreamTransform()
  1183 + {
  1184 + this->collector = QSharedPointer<CollectSets>(new CollectSets());
  1185 + }
  1186 +
1261 ~DirectStreamTransform() 1187 ~DirectStreamTransform()
1262 { 1188 {
1263 // Delete all the stages 1189 // Delete all the stages
@@ -1270,8 +1196,9 @@ public: @@ -1270,8 +1196,9 @@ public:
1270 protected: 1196 protected:
1271 QList<bool> stage_variance; 1197 QList<bool> stage_variance;
1272 1198
1273 - FirstStage * readStage;  
1274 - LastStage * collectionStage; 1199 + ReadStage * readStage;
  1200 + SingleThreadStage * collectionStage;
  1201 + QSharedPointer<CollectSets> collector;
1275 1202
1276 QList<ProcessingStage *> processingStages; 1203 QList<ProcessingStage *> processingStages;
1277 1204
@@ -1311,21 +1238,20 @@ QMutex DirectStreamTransform::poolsAccess; @@ -1311,21 +1238,20 @@ QMutex DirectStreamTransform::poolsAccess;
1311 1238
1312 BR_REGISTER(Transform, DirectStreamTransform) 1239 BR_REGISTER(Transform, DirectStreamTransform)
1313 1240
1314 -;  
1315 -  
1316 -class StreamTransform : public TimeVaryingTransform 1241 +class StreamTransform : public WrapperTransform
1317 { 1242 {
1318 Q_OBJECT 1243 Q_OBJECT
1319 1244
1320 public: 1245 public:
1321 - StreamTransform() : TimeVaryingTransform(false) 1246 + StreamTransform() : WrapperTransform(false)
1322 { 1247 {
1323 } 1248 }
1324 1249
1325 - Q_PROPERTY(br::Transform *transform READ get_transform WRITE set_transform RESET reset_transform STORED false)  
1326 Q_PROPERTY(int activeFrames READ get_activeFrames WRITE set_activeFrames RESET reset_activeFrames) 1250 Q_PROPERTY(int activeFrames READ get_activeFrames WRITE set_activeFrames RESET reset_activeFrames)
1327 - BR_PROPERTY(br::Transform *, transform, NULL) 1251 + Q_PROPERTY(br::Idiocy::StreamModes readMode READ get_readMode WRITE set_readMode RESET reset_readMode)
  1252 +
1328 BR_PROPERTY(int, activeFrames, 100) 1253 BR_PROPERTY(int, activeFrames, 100)
  1254 + BR_PROPERTY(br::Idiocy::StreamModes, readMode, br::Idiocy::Auto)
1329 1255
1330 bool timeVarying() const { return true; } 1256 bool timeVarying() const { return true; }
1331 1257
@@ -1366,6 +1292,7 @@ public: @@ -1366,6 +1292,7 @@ public:
1366 basis.setParent(this->parent()); 1292 basis.setParent(this->parent());
1367 basis.transforms.clear(); 1293 basis.transforms.clear();
1368 basis.activeFrames = this->activeFrames; 1294 basis.activeFrames = this->activeFrames;
  1295 + basis.readMode = this->readMode;
1369 1296
1370 // We need at least a CompositeTransform * to acess transform's children. 1297 // We need at least a CompositeTransform * to acess transform's children.
1371 CompositeTransform * downcast = dynamic_cast<CompositeTransform *> (transform); 1298 CompositeTransform * downcast = dynamic_cast<CompositeTransform *> (transform);
@@ -1448,8 +1375,6 @@ private: @@ -1448,8 +1375,6 @@ private:
1448 1375
1449 BR_REGISTER(Transform, StreamTransform) 1376 BR_REGISTER(Transform, StreamTransform)
1450 1377
1451 -  
1452 -  
1453 } // namespace br 1378 } // namespace br
1454 1379
1455 #include "stream.moc" 1380 #include "stream.moc"
scripts/pedestrianBaselineLBP.sh
1 #!/bin/bash 1 #!/bin/bash
2 2
3 -#Right now this is just a simple proof of concept. No quanititative eval is performed 3 +# Right now this is just a simple proof of concept. No quantitative eval is performed
4 # but instead the qualitative results are displayed. 4 # but instead the qualitative results are displayed.
5 5
6 -#Make sure you set your data path. This will likely by your openbr/data directory.  
7 -INRIA_PATH=$DATA/INRIAPerson 6 +# Make sure you set your data path. This will likely by your openbr/data directory.
  7 +if [ -z "$DATA" ]; then
  8 + INRIA_PATH=../data/INRIAPerson
  9 +else
  10 + INRIA_PATH=$DATA/INRIAPerson
  11 +fi
  12 +
  13 +ALG="Open+Cvt(Gray)+Rename(neg,0)+BuildScales(Blur(2)+LBP(1,2)+SlidingWindow(Hist(59)+Cat+LDA(isBinary=true),windowWidth=10,takeLargestScale=false,threshold=2),windowWidth=10,takeLargestScale=false,minScale=4)+ConsolidateDetections+Discard"
  14 +
  15 +# Josh's new algorithm in progress
  16 +# ALG2="Open+Cvt(Gray)+Gradient+Bin(0,360,9,true)+Merge+Integral+IntegralDetector"
8 17
9 br -useGui 0 \ 18 br -useGui 0 \
10 - -algorithm "Open+Cvt(Gray)+Rename(neg,0)+BuildScales(Blur(2)+LBP(1,2)+SlidingWindow(Hist(59)+Cat+LDA(isBinary=true),windowWidth=10,takeLargestScale=false,threshold=3),windowWidth=10,takeLargestScale=false,minScale=4)+Discard" \ 19 + -algorithm "${ALG}" \
  20 + -path $INRIA_PATH/img \
  21 + -train $INRIA_PATH/sigset/train.xml pedModel
  22 +
  23 +br -algorithm pedModel \
11 -path $INRIA_PATH/img \ 24 -path $INRIA_PATH/img \
12 - -train $INRIA_PATH/sigset/train.xml pedModel \  
13 -enroll $INRIA_PATH/sigset/testSmall.xml pedResults.xml 25 -enroll $INRIA_PATH/sigset/testSmall.xml pedResults.xml
14 26
15 -br -parallelism 0 -algorithm Open+Draw+Show -path /Users/m29389/Data/INRIAPerson/img/ -enroll pedResults.xml 27 +br -parallelism 0 -algorithm Open+Draw+Show -path $INRIA_PATH/img -enroll pedResults.xml