Commit 7685e92fa6b8a203861b22aa90362d47c7553805

Authored by Josh Klontz
2 parents 6dc7d8cd e2a5bc7e

Merge pull request #191 from biometrics/streaming_gallery

Preliminary work on not loading complete galleries during enrollment
openbr/core/core.cpp
@@ -134,55 +134,51 @@ struct AlgorithmCore @@ -134,55 +134,51 @@ struct AlgorithmCore
134 if (input.name.isEmpty()) return FileList(); 134 if (input.name.isEmpty()) return FileList();
135 else gallery = getMemoryGallery(input); 135 else gallery = getMemoryGallery(input);
136 } 136 }
137 - TemplateList data(TemplateList::fromGallery(input));  
138 137
139 bool multiProcess = Globals->file.getBool("multiProcess", false); 138 bool multiProcess = Globals->file.getBool("multiProcess", false);
  139 + bool fileExclusion = false;
140 140
141 - if (gallery.contains("append"))  
142 - {  
143 - // Remove any templates which are already in the gallery  
144 - QScopedPointer<Gallery> g(Gallery::make(gallery));  
145 - files = g->files();  
146 - QSet<QString> nameSet = QSet<QString>::fromList(files.names());  
147 - for (int i = data.size() - 1; i>=0; i--) {  
148 - if (nameSet.contains(data[i].file.name))  
149 - {  
150 - data.removeAt(i);  
151 - }  
152 - } 141 + // In append mode, we will exclude any templates with filenames already present in the output gallery
  142 + if (gallery.contains("append") && gallery.exists() ) {
  143 + FileList::fromGallery(gallery,true);
  144 + fileExclusion = true;
153 } 145 }
154 146
155 - if (data.empty())  
156 - return files; 147 + Gallery * temp = Gallery::make(input);
  148 + qint64 total = temp->totalSize();
157 149
158 - // Store steps for ProgressCounter  
159 Globals->currentStep = 0; 150 Globals->currentStep = 0;
160 - Globals->totalSteps = data.length(); 151 + Globals->totalSteps = total;
161 152
162 QScopedPointer<Transform> basePipe; 153 QScopedPointer<Transform> basePipe;
163 154
164 - if (!multiProcess)  
165 - {  
166 - QString pipeDesc = "GalleryOutput("+gallery.flat()+")+ProgressCounter("+QString::number(data.length())+")+Discard"; 155 + QString pipeDesc = "GalleryOutput("+gallery.flat()+")+ProgressCounter("+QString::number(total)+")+Discard";
  156 +
  157 + if (!multiProcess) {
167 basePipe.reset(Transform::make(pipeDesc,NULL)); 158 basePipe.reset(Transform::make(pipeDesc,NULL));
168 CompositeTransform * downcast = dynamic_cast<CompositeTransform *>(basePipe.data()); 159 CompositeTransform * downcast = dynamic_cast<CompositeTransform *>(basePipe.data());
169 - if (downcast == NULL)  
170 - qFatal("downcast failed?");  
171 160
172 - // replace that placeholder with the current algorithm 161 + if (downcast == NULL) qFatal("downcast failed?");
  162 +
173 downcast->transforms.prepend(this->transform.data()); 163 downcast->transforms.prepend(this->transform.data());
  164 + if (fileExclusion) {
  165 + Transform * temp = Transform::make("FileExclusion(" + gallery.flat() + ")", downcast);
  166 + downcast->transforms.prepend(temp);
  167 + }
174 168
175 // call init on the pipe to collapse the algorithm (if its top level is a pipe) 169 // call init on the pipe to collapse the algorithm (if its top level is a pipe)
176 downcast->init(); 170 downcast->init();
177 } 171 }
178 - else  
179 - {  
180 - QString pipeDesc = "ProcessWrapper("+transformString+")"+"+GalleryOutput("+gallery.flat()+")+ProgressCounter("+QString::number(data.length())+")+Discard"; 172 + else {
  173 + pipeDesc = "ProcessWrapper("+transformString+")"+pipeDesc;
  174 + if (fileExclusion)
  175 + pipeDesc = "FileExclusion(" + gallery.flat() +")" + pipeDesc;
  176 +
181 basePipe.reset(Transform::make(pipeDesc,NULL)); 177 basePipe.reset(Transform::make(pipeDesc,NULL));
182 } 178 }
183 179
184 // Next, we make a Stream (with placeholder transform) 180 // Next, we make a Stream (with placeholder transform)
185 - QString streamDesc = "Stream(readMode=DistributeFrames)"; 181 + QString streamDesc = "Stream(readMode=StreamGallery)";
186 QScopedPointer<Transform> baseStream(Transform::make(streamDesc, NULL)); 182 QScopedPointer<Transform> baseStream(Transform::make(streamDesc, NULL));
187 WrapperTransform * wrapper = dynamic_cast<WrapperTransform *> (baseStream.data()); 183 WrapperTransform * wrapper = dynamic_cast<WrapperTransform *> (baseStream.data());
188 184
@@ -194,9 +190,11 @@ struct AlgorithmCore @@ -194,9 +190,11 @@ struct AlgorithmCore
194 190
195 Globals->startTime.start(); 191 Globals->startTime.start();
196 192
197 - wrapper->projectUpdate(data,data); 193 + TemplateList data, output;
  194 + data.append(input);
  195 + wrapper->projectUpdate(data, output);
198 196
199 - files.append(data.files()); 197 + files.append(output.files());
200 198
201 return files; 199 return files;
202 } 200 }
@@ -337,13 +335,9 @@ struct AlgorithmCore @@ -337,13 +335,9 @@ struct AlgorithmCore
337 // comparison against the smaller gallery (which will be enrolled, and stored in memory). 335 // comparison against the smaller gallery (which will be enrolled, and stored in memory).
338 bool needEnrollRows = false; 336 bool needEnrollRows = false;
339 337
340 -  
341 -  
342 -  
343 if (output.exists() && output.get<bool>("cache", false)) return; 338 if (output.exists() && output.get<bool>("cache", false)) return;
344 if (queryGallery == ".") queryGallery = targetGallery; 339 if (queryGallery == ".") queryGallery = targetGallery;
345 340
346 -  
347 // To decide which gallery is larger, we need to read both, but at this point we just want the 341 // To decide which gallery is larger, we need to read both, but at this point we just want the
348 // metadata, and don't need the enrolled matrices. 342 // metadata, and don't need the enrolled matrices.
349 FileList targetMetadata; 343 FileList targetMetadata;
@@ -359,15 +353,21 @@ struct AlgorithmCore @@ -359,15 +353,21 @@ struct AlgorithmCore
359 353
360 File rowGallery = queryGallery; 354 File rowGallery = queryGallery;
361 File colGallery = targetGallery; 355 File colGallery = targetGallery;
362 - int rowSize = queryMetadata.size(); 356 + qint64 rowSize;
363 357
  358 + Gallery * temp;
364 if (transposeMode) 359 if (transposeMode)
365 { 360 {
366 rowGallery = targetGallery; 361 rowGallery = targetGallery;
367 colGallery = queryGallery; 362 colGallery = queryGallery;
368 - rowSize = targetMetadata.size(); 363 + temp = Gallery::make(targetGallery);
369 } 364 }
370 - 365 + else
  366 + {
  367 + temp = Gallery::make(queryGallery);
  368 + }
  369 + rowSize = temp->totalSize();
  370 + delete temp;
371 371
372 // Is the column gallery already enrolled? We keep the enrolled column gallery in memory, and in multi-process 372 // Is the column gallery already enrolled? We keep the enrolled column gallery in memory, and in multi-process
373 // mode, every worker process retains a copy of this gallery in memory. When not in multi-process mode, we can 373 // mode, every worker process retains a copy of this gallery in memory. When not in multi-process mode, we can
@@ -423,8 +423,6 @@ struct AlgorithmCore @@ -423,8 +423,6 @@ struct AlgorithmCore
423 // progress counting step. 423 // progress counting step.
424 // After the base algorithm is built, the whole thing will be run in a stream, so that I/O can be handled sequentially. 424 // After the base algorithm is built, the whole thing will be run in a stream, so that I/O can be handled sequentially.
425 425
426 -  
427 -  
428 // The actual comparison step is done by a GalleryCompare transform, which has a Distance, and a gallery as data. 426 // The actual comparison step is done by a GalleryCompare transform, which has a Distance, and a gallery as data.
429 // Incoming templates are compared against the templates in the gallery, and the output is the resulting score 427 // Incoming templates are compared against the templates in the gallery, and the output is the resulting score
430 // vector. 428 // vector.
openbr/openbr_plugin.cpp
@@ -865,14 +865,14 @@ void br::Context::printStatus() @@ -865,14 +865,14 @@ void br::Context::printStatus()
865 const float p = progress(); 865 const float p = progress();
866 if (p < 1) { 866 if (p < 1) {
867 int s = timeRemaining(); 867 int s = timeRemaining();
868 - fprintf(stderr, "%05.2f%% ELAPSED=%s REMAINING=%s COUNT=%g/%g \r", p*100, QtUtils::toTime(Globals->startTime.elapsed()/1000.0f).toStdString().c_str(), QtUtils::toTime(s).toStdString().c_str(), Globals->currentStep, Globals->totalSteps); 868 + fprintf(stderr,"%05.2f%% ELAPSED=%s REMAINING=%s COUNT=%g \r", p*100, QtUtils::toTime(Globals->startTime.elapsed()/1000.0f).toStdString().c_str(), QtUtils::toTime(s).toStdString().c_str(), Globals->currentStep);
869 } 869 }
870 } 870 }
871 871
872 float br::Context::progress() const 872 float br::Context::progress() const
873 { 873 {
874 if (totalSteps == 0) return -1; 874 if (totalSteps == 0) return -1;
875 - return currentStep / totalSteps; 875 + return currentProgress / totalSteps;
876 } 876 }
877 877
878 void br::Context::setProperty(const QString &key, const QString &value) 878 void br::Context::setProperty(const QString &key, const QString &value)
openbr/openbr_plugin.h
@@ -687,6 +687,10 @@ public: @@ -687,6 +687,10 @@ public:
687 Q_PROPERTY(double currentStep READ get_currentStep WRITE set_currentStep RESET reset_currentStep) 687 Q_PROPERTY(double currentStep READ get_currentStep WRITE set_currentStep RESET reset_currentStep)
688 BR_PROPERTY(double, currentStep, 0) 688 BR_PROPERTY(double, currentStep, 0)
689 689
  690 + Q_PROPERTY(double currentProgress READ get_currentProgress WRITE set_currentProgress RESET reset_currentProgress)
  691 + BR_PROPERTY(double, currentProgress, 0)
  692 +
  693 +
690 /*! 694 /*!
691 * \brief Used internally to compute progress() and timeRemaining(). 695 * \brief Used internally to compute progress() and timeRemaining().
692 */ 696 */
@@ -1095,6 +1099,9 @@ public: @@ -1095,6 +1099,9 @@ public:
1095 static Gallery *make(const File &file); /*!< \brief Make a gallery to/from a file on disk. */ 1099 static Gallery *make(const File &file); /*!< \brief Make a gallery to/from a file on disk. */
1096 void init(); 1100 void init();
1097 1101
  1102 + virtual qint64 totalSize() { return std::numeric_limits<qint64>::max(); }
  1103 + virtual qint64 position() { return 0; }
  1104 +
1098 private: 1105 private:
1099 QSharedPointer<Gallery> next; 1106 QSharedPointer<Gallery> next;
1100 }; 1107 };
openbr/plugins/gallery.cpp
@@ -23,6 +23,7 @@ @@ -23,6 +23,7 @@
23 #include <QSqlError> 23 #include <QSqlError>
24 #include <QSqlQuery> 24 #include <QSqlQuery>
25 #include <QSqlRecord> 25 #include <QSqlRecord>
  26 +#include <QXmlStreamReader>
26 #endif // BR_EMBEDDED 27 #endif // BR_EMBEDDED
27 #include <opencv2/highgui/highgui.hpp> 28 #include <opencv2/highgui/highgui.hpp>
28 #include "openbr_internal.h" 29 #include "openbr_internal.h"
@@ -127,6 +128,7 @@ class galGallery : public Gallery @@ -127,6 +128,7 @@ class galGallery : public Gallery
127 Template m; 128 Template m;
128 stream >> m; 129 stream >> m;
129 templates.append(m); 130 templates.append(m);
  131 + templates.last().file.set("progress", totalSize());
130 } 132 }
131 133
132 *done = stream.atEnd(); 134 *done = stream.atEnd();
@@ -140,6 +142,17 @@ class galGallery : public Gallery @@ -140,6 +142,17 @@ class galGallery : public Gallery
140 142
141 stream << t; 143 stream << t;
142 } 144 }
  145 +
  146 + qint64 totalSize()
  147 + {
  148 + return gallery.size();
  149 + }
  150 +
  151 + qint64 position()
  152 + {
  153 + return gallery.pos();
  154 + }
  155 +
143 }; 156 };
144 157
145 BR_REGISTER(Gallery, galGallery) 158 BR_REGISTER(Gallery, galGallery)
@@ -328,6 +341,7 @@ class memGallery : public Gallery @@ -328,6 +341,7 @@ class memGallery : public Gallery
328 { 341 {
329 Q_OBJECT 342 Q_OBJECT
330 int block; 343 int block;
  344 + qint64 gallerySize;
331 345
332 void init() 346 void init()
333 { 347 {
@@ -338,6 +352,7 @@ class memGallery : public Gallery @@ -338,6 +352,7 @@ class memGallery : public Gallery
338 MemoryGalleries::galleries[file] = gallery->read(); 352 MemoryGalleries::galleries[file] = gallery->read();
339 align(MemoryGalleries::galleries[file]); 353 align(MemoryGalleries::galleries[file]);
340 MemoryGalleries::aligned[file] = true; 354 MemoryGalleries::aligned[file] = true;
  355 + gallerySize = MemoryGalleries::galleries[file].size();
341 } 356 }
342 } 357 }
343 358
@@ -349,6 +364,10 @@ class memGallery : public Gallery @@ -349,6 +364,10 @@ class memGallery : public Gallery
349 } 364 }
350 365
351 TemplateList templates = MemoryGalleries::galleries[file].mid(block*readBlockSize, readBlockSize); 366 TemplateList templates = MemoryGalleries::galleries[file].mid(block*readBlockSize, readBlockSize);
  367 + for (qint64 i = 0; i < templates.size();i++) {
  368 + templates[i].file.set("progress", i + block * readBlockSize);
  369 + }
  370 +
352 *done = (templates.size() < readBlockSize); 371 *done = (templates.size() < readBlockSize);
353 block = *done ? 0 : block+1; 372 block = *done ? 0 : block+1;
354 return templates; 373 return templates;
@@ -389,6 +408,16 @@ class memGallery : public Gallery @@ -389,6 +408,16 @@ class memGallery : public Gallery
389 templates.alignedData = alignedData; 408 templates.alignedData = alignedData;
390 } 409 }
391 410
  411 + qint64 totalSize()
  412 + {
  413 + return gallerySize;
  414 + }
  415 +
  416 + qint64 position()
  417 + {
  418 + return block * readBlockSize;
  419 + }
  420 +
392 }; 421 };
393 422
394 BR_REGISTER(Gallery, memGallery) 423 BR_REGISTER(Gallery, memGallery)
@@ -449,16 +478,19 @@ FileList FileList::fromGallery(const File &amp; file, bool cache) @@ -449,16 +478,19 @@ FileList FileList::fromGallery(const File &amp; file, bool cache)
449 * 478 *
450 * \see txtGallery 479 * \see txtGallery
451 */ 480 */
452 -class csvGallery : public Gallery 481 +class csvGallery : public FileGallery
453 { 482 {
454 Q_OBJECT 483 Q_OBJECT
455 Q_PROPERTY(int fileIndex READ get_fileIndex WRITE set_fileIndex RESET reset_fileIndex) 484 Q_PROPERTY(int fileIndex READ get_fileIndex WRITE set_fileIndex RESET reset_fileIndex)
456 BR_PROPERTY(int, fileIndex, 0) 485 BR_PROPERTY(int, fileIndex, 0)
457 486
458 FileList files; 487 FileList files;
  488 + QStringList headers;
459 489
460 ~csvGallery() 490 ~csvGallery()
461 { 491 {
  492 + f.close();
  493 +
462 if (files.isEmpty()) return; 494 if (files.isEmpty()) return;
463 495
464 QMap<QString,QVariant> samples; 496 QMap<QString,QVariant> samples;
@@ -496,25 +528,37 @@ class csvGallery : public Gallery @@ -496,25 +528,37 @@ class csvGallery : public Gallery
496 528
497 TemplateList readBlock(bool *done) 529 TemplateList readBlock(bool *done)
498 { 530 {
499 - *done = true; 531 + *done = false;
500 TemplateList templates; 532 TemplateList templates;
501 - if (!file.exists()) return templates;  
502 -  
503 - QStringList lines = QtUtils::readLines(file); 533 + if (!file.exists()) {
  534 + *done = true;
  535 + return templates;
  536 + }
504 QRegExp regexp("\\s*,\\s*"); 537 QRegExp regexp("\\s*,\\s*");
505 - QStringList headers;  
506 - if (!lines.isEmpty()) headers = lines.takeFirst().split(regexp);  
507 538
508 - foreach (const QString &line, lines) { 539 + if (f.pos() == 0)
  540 + {
  541 + // read a line
  542 + QByteArray lineBytes = f.readLine();
  543 + QString line = QString::fromLocal8Bit(lineBytes).trimmed();
  544 + headers = line.split(regexp);
  545 + }
  546 +
  547 + for (qint64 i = 0; i < this->readBlockSize && !f.atEnd(); i++){
  548 + QByteArray lineBytes = f.readLine();
  549 + QString line = QString::fromLocal8Bit(lineBytes).trimmed();
  550 +
509 QStringList words = line.split(regexp); 551 QStringList words = line.split(regexp);
510 if (words.size() != headers.size()) continue; 552 if (words.size() != headers.size()) continue;
511 - File f;  
512 - for (int i=0; i<words.size(); i++) {  
513 - if (i == 0) f.name = words[i];  
514 - else f.set(headers[i], words[i]); 553 + File fi;
  554 + for (int j=0; j<words.size(); j++) {
  555 + if (j == 0) fi.name = words[j];
  556 + else fi.set(headers[j], words[j]);
515 } 557 }
516 - templates.append(f); 558 + templates.append(fi);
  559 + templates.last().file.set("progress", f.pos());
517 } 560 }
  561 + *done = f.atEnd();
518 562
519 return templates; 563 return templates;
520 } 564 }
@@ -568,18 +612,12 @@ BR_REGISTER(Gallery, csvGallery) @@ -568,18 +612,12 @@ BR_REGISTER(Gallery, csvGallery)
568 \endverbatim 612 \endverbatim
569 * \see csvGallery 613 * \see csvGallery
570 */ 614 */
571 -class txtGallery : public Gallery 615 +class txtGallery : public FileGallery
572 { 616 {
573 Q_OBJECT 617 Q_OBJECT
574 Q_PROPERTY(QString label READ get_label WRITE set_label RESET reset_label STORED false) 618 Q_PROPERTY(QString label READ get_label WRITE set_label RESET reset_label STORED false)
575 BR_PROPERTY(QString, label, "") 619 BR_PROPERTY(QString, label, "")
576 620
577 - QFile f;  
578 - ~txtGallery()  
579 - {  
580 - f.close();  
581 - }  
582 -  
583 TemplateList readBlock(bool *done) 621 TemplateList readBlock(bool *done)
584 { 622 {
585 *done = false; 623 *done = false;
@@ -597,6 +635,7 @@ class txtGallery : public Gallery @@ -597,6 +635,7 @@ class txtGallery : public Gallery
597 int splitIndex = line.lastIndexOf(' '); 635 int splitIndex = line.lastIndexOf(' ');
598 if (splitIndex == -1) templates.append(File(line)); 636 if (splitIndex == -1) templates.append(File(line));
599 else templates.append(File(line.mid(0, splitIndex), line.mid(splitIndex+1))); 637 else templates.append(File(line.mid(0, splitIndex), line.mid(splitIndex+1)));
  638 + templates.last().file.set("progress", this->position());
600 } 639 }
601 640
602 if (f.atEnd()) { 641 if (f.atEnd()) {
@@ -608,14 +647,6 @@ class txtGallery : public Gallery @@ -608,14 +647,6 @@ class txtGallery : public Gallery
608 return templates; 647 return templates;
609 } 648 }
610 649
611 - void init()  
612 - {  
613 - f.setFileName(file);  
614 - QtUtils::touchDir(f);  
615 - if (!f.open(QFile::ReadWrite))  
616 - qFatal("Failed to open %s for read/write.", qPrintable(file));  
617 - }  
618 -  
619 void write(const Template &t) 650 void write(const Template &t)
620 { 651 {
621 QString line = t.file.name; 652 QString line = t.file.name;
@@ -627,21 +658,16 @@ class txtGallery : public Gallery @@ -627,21 +658,16 @@ class txtGallery : public Gallery
627 }; 658 };
628 659
629 BR_REGISTER(Gallery, txtGallery) 660 BR_REGISTER(Gallery, txtGallery)
  661 +
630 /*! 662 /*!
631 * \ingroup galleries 663 * \ingroup galleries
632 * \brief Treats each line as a call to File::flat() 664 * \brief Treats each line as a call to File::flat()
633 * \author Josh Klontz \cite jklontz 665 * \author Josh Klontz \cite jklontz
634 */ 666 */
635 -class flatGallery : public Gallery 667 +class flatGallery : public FileGallery
636 { 668 {
637 Q_OBJECT 669 Q_OBJECT
638 670
639 - QFile f;  
640 - ~flatGallery()  
641 - {  
642 - f.close();  
643 - }  
644 -  
645 TemplateList readBlock(bool *done) 671 TemplateList readBlock(bool *done)
646 { 672 {
647 *done = false; 673 *done = false;
@@ -654,8 +680,10 @@ class flatGallery : public Gallery @@ -654,8 +680,10 @@ class flatGallery : public Gallery
654 { 680 {
655 QByteArray line = f.readLine(); 681 QByteArray line = f.readLine();
656 682
657 - if (!line.isEmpty()) 683 + if (!line.isEmpty()) {
658 templates.append(File(QString::fromLocal8Bit(line).trimmed())); 684 templates.append(File(QString::fromLocal8Bit(line).trimmed()));
  685 + templates.last().file.set("progress", this->position());
  686 + }
659 687
660 if (f.atEnd()) { 688 if (f.atEnd()) {
661 *done=true; 689 *done=true;
@@ -666,15 +694,6 @@ class flatGallery : public Gallery @@ -666,15 +694,6 @@ class flatGallery : public Gallery
666 return templates; 694 return templates;
667 } 695 }
668 696
669 - void init()  
670 - {  
671 - f.setFileName(file);  
672 - QtUtils::touchDir(f);  
673 - if (!f.open(QFile::ReadWrite))  
674 - qFatal("Failed to open %s for read/write.", qPrintable(file));  
675 -  
676 - }  
677 -  
678 void write(const Template &t) 697 void write(const Template &t)
679 { 698 {
680 f.write((t.file.flat()+"\n").toLocal8Bit() ); 699 f.write((t.file.flat()+"\n").toLocal8Bit() );
@@ -688,29 +707,140 @@ BR_REGISTER(Gallery, flatGallery) @@ -688,29 +707,140 @@ BR_REGISTER(Gallery, flatGallery)
688 * \brief A \ref sigset input. 707 * \brief A \ref sigset input.
689 * \author Josh Klontz \cite jklontz 708 * \author Josh Klontz \cite jklontz
690 */ 709 */
691 -class xmlGallery : public Gallery 710 +class xmlGallery : public FileGallery
692 { 711 {
693 Q_OBJECT 712 Q_OBJECT
694 Q_PROPERTY(bool ignoreMetadata READ get_ignoreMetadata WRITE set_ignoreMetadata RESET reset_ignoreMetadata STORED false) 713 Q_PROPERTY(bool ignoreMetadata READ get_ignoreMetadata WRITE set_ignoreMetadata RESET reset_ignoreMetadata STORED false)
695 BR_PROPERTY(bool, ignoreMetadata, false) 714 BR_PROPERTY(bool, ignoreMetadata, false)
696 FileList files; 715 FileList files;
697 716
  717 + QXmlStreamReader reader;
  718 +
  719 + QString currentSignatureName;
  720 + bool signatureActive;
  721 +
698 ~xmlGallery() 722 ~xmlGallery()
699 { 723 {
  724 + f.close();
700 if (!files.isEmpty()) 725 if (!files.isEmpty())
701 BEE::writeSigset(file, files, ignoreMetadata); 726 BEE::writeSigset(file, files, ignoreMetadata);
702 } 727 }
703 728
704 TemplateList readBlock(bool *done) 729 TemplateList readBlock(bool *done)
705 { 730 {
  731 + if (reader.atEnd())
  732 + f.seek(0);
  733 +
  734 + TemplateList templates;
  735 + qint64 count = 0;
  736 + while (!reader.atEnd())
  737 + {
  738 + // if an identity is active we try to read presentations
  739 + if (signatureActive)
  740 + {
  741 + while (signatureActive)
  742 + {
  743 + QXmlStreamReader::TokenType signatureToken = reader.readNext();
  744 +
  745 + // did the signature end?
  746 + if (signatureToken == QXmlStreamReader::EndElement && reader.name() == "biometric-signature") {
  747 + signatureActive = false;
  748 + break;
  749 + }
  750 + // did we reach the end of the document? Theoretically this shoudln't happen without reaching the end of
  751 + if (signatureToken == QXmlStreamReader::EndDocument)
  752 + break;
  753 +
  754 + // a presentation!
  755 + if (signatureToken == QXmlStreamReader::StartElement && reader.name() == "presentation") {
  756 + templates.append(Template(File("",currentSignatureName)));
  757 + foreach (const QXmlStreamAttribute & attribute, reader.attributes()) {
  758 + // file-name is stored directly on file, not as a key/value pair
  759 + if (attribute.name() == "file-name")
  760 + templates.last().file.name = attribute.value().toString();
  761 + // other values are directly set as metadata
  762 + else if (!ignoreMetadata) templates.last().file.set(attribute.name().toString(), attribute.value().toString());
  763 + }
  764 +
  765 + // a presentation can have bounding boxes as child elements
  766 + bool signatureActive = true;
  767 + QList<QRectF> rects = templates.last().file.rects();
  768 + while (signatureActive)
  769 + {
  770 + QXmlStreamReader::TokenType pToken = reader.readNext();
  771 + if (pToken == QXmlStreamReader::EndElement && reader.name() == "presentation")
  772 + break;
  773 +
  774 + if (pToken == QXmlStreamReader::StartElement)
  775 + {
  776 + // get boudning box properties as attributes, just going to assume this all works
  777 + qreal x = reader.attributes().value("x").toDouble();
  778 + qreal y = reader.attributes().value("y").toDouble();
  779 + qreal width = reader.attributes().value("width").toDouble();
  780 + qreal height = reader.attributes().value("height").toDouble();
  781 + rects += QRectF(x, y, width, height);
  782 + }
  783 + }
  784 + templates.last().file.setRects(rects);
  785 + templates.last().file.set("progress", f.pos());
  786 +
  787 + count++;
  788 + if (count >= this->readBlockSize) {
  789 + *done = false;
  790 + return templates;
  791 + }
  792 + }
  793 + }
  794 + }
  795 + // otherwise, keep reading elements until the next identity is reacehed
  796 + else
  797 + {
  798 + QXmlStreamReader::TokenType token = reader.readNext();
  799 +
  800 + // end of file?
  801 + if (token == QXmlStreamReader::EndDocument)
  802 + break;
  803 +
  804 + // we are only interested in new elements
  805 + if (token != QXmlStreamReader::StartElement)
  806 + continue;
  807 +
  808 + QStringRef elName = reader.name();
  809 +
  810 + // biometric-signature-set is the root element
  811 + if (elName == "biometric-signature-set")
  812 + continue;
  813 +
  814 + // biometric-signature -- an identity
  815 + if (elName == "biometric-signature")
  816 + {
  817 + // read the name associated with the current signature
  818 + if (!reader.attributes().hasAttribute("name"))
  819 + {
  820 + qDebug() << "Biometric signature missing name";
  821 + continue;
  822 + }
  823 + currentSignatureName = reader.attributes().value("name").toString();
  824 + signatureActive = true;
  825 + }
  826 + }
  827 +
  828 + }
706 *done = true; 829 *done = true;
707 - return TemplateList(BEE::readSigset(file, ignoreMetadata)); 830 +
  831 + return templates;
708 } 832 }
709 833
710 void write(const Template &t) 834 void write(const Template &t)
711 { 835 {
712 files.append(t.file); 836 files.append(t.file);
713 } 837 }
  838 +
  839 + void init()
  840 + {
  841 + FileGallery::init();
  842 + reader.setDevice(&f);
  843 + }
714 }; 844 };
715 845
716 BR_REGISTER(Gallery, xmlGallery) 846 BR_REGISTER(Gallery, xmlGallery)
@@ -1178,6 +1308,17 @@ BR_REGISTER(Gallery, vbbGallery) @@ -1178,6 +1308,17 @@ BR_REGISTER(Gallery, vbbGallery)
1178 1308
1179 #endif 1309 #endif
1180 1310
  1311 +void FileGallery::init()
  1312 +{
  1313 + f.setFileName(file);
  1314 + QtUtils::touchDir(f);
  1315 + if (!f.open(QFile::ReadWrite))
  1316 + qFatal("Failed to open %s for read/write.", qPrintable(file));
  1317 + fileSize = f.size();
  1318 +
  1319 + Gallery::init();
  1320 +}
  1321 +
1181 } // namespace br 1322 } // namespace br
1182 1323
1183 #include "gallery.moc" 1324 #include "gallery.moc"
openbr/plugins/misc.cpp
@@ -517,14 +517,18 @@ class ProgressCounterTransform : public TimeVaryingTransform @@ -517,14 +517,18 @@ class ProgressCounterTransform : public TimeVaryingTransform
517 517
518 qint64 elapsed = timer.elapsed(); 518 qint64 elapsed = timer.elapsed();
519 519
  520 + if (!dst.empty()) {
  521 + Globals->currentProgress = dst.last().file.get<qint64>("progress",0);
  522 + dst.last().file.remove("progress");
  523 + Globals->currentStep++;
  524 + }
  525 +
520 // updated every second 526 // updated every second
521 if (elapsed > 1000) { 527 if (elapsed > 1000) {
522 Globals->printStatus(); 528 Globals->printStatus();
523 timer.start(); 529 timer.start();
524 } 530 }
525 531
526 - Globals->currentStep++;  
527 -  
528 return; 532 return;
529 } 533 }
530 534
@@ -537,12 +541,13 @@ class ProgressCounterTransform : public TimeVaryingTransform @@ -537,12 +541,13 @@ class ProgressCounterTransform : public TimeVaryingTransform
537 { 541 {
538 (void) data; 542 (void) data;
539 float p = br_progress(); 543 float p = br_progress();
540 - qDebug("%05.2f%% ELAPSED=%s REMAINING=%s COUNT=%g/%g \r", p*100, QtUtils::toTime(Globals->startTime.elapsed()/1000.0f).toStdString().c_str(), QtUtils::toTime(0).toStdString().c_str(), Globals->currentStep, Globals->totalSteps); 544 + qDebug("%05.2f%% ELAPSED=%s REMAINING=%s COUNT=%g \r", p*100, QtUtils::toTime(Globals->startTime.elapsed()/1000.0f).toStdString().c_str(), QtUtils::toTime(0).toStdString().c_str(), Globals->currentStep);
541 } 545 }
542 546
543 void init() 547 void init()
544 { 548 {
545 timer.start(); 549 timer.start();
  550 + Globals->currentStep = 0;
546 } 551 }
547 552
548 public: 553 public:
@@ -689,6 +694,40 @@ public: @@ -689,6 +694,40 @@ public:
689 694
690 BR_REGISTER(Transform, OutputTransform) 695 BR_REGISTER(Transform, OutputTransform)
691 696
  697 +class FileExclusionTransform : public UntrainableMetaTransform
  698 +{
  699 + Q_OBJECT
  700 +
  701 + Q_PROPERTY(QString exclusionGallery READ get_exclusionGallery WRITE set_exclusionGallery RESET reset_exclusionGallery STORED false)
  702 + BR_PROPERTY(QString, exclusionGallery, "")
  703 +
  704 + QSet<QString> excluded;
  705 +
  706 + void project(const Template & src, Template & dst) const
  707 + {
  708 + qFatal("FileExclusion can't do anything here");
  709 + }
  710 +
  711 + void project(const TemplateList &src, TemplateList &dst) const
  712 + {
  713 + foreach(const Template & srcTemp, src)
  714 + {
  715 + if (!excluded.contains(srcTemp.file))
  716 + dst.append(srcTemp);
  717 + }
  718 + }
  719 +
  720 + void init()
  721 + {
  722 + if (exclusionGallery.isEmpty())
  723 + return;
  724 + FileList temp = FileList::fromGallery(exclusionGallery);
  725 + excluded = QSet<QString>::fromList(temp.names());
  726 + }
  727 +};
  728 +
  729 +BR_REGISTER(Transform, FileExclusionTransform)
  730 +
692 } 731 }
693 732
694 #include "misc.moc" 733 #include "misc.moc"
openbr/plugins/openbr_internal.h
@@ -345,6 +345,21 @@ protected: @@ -345,6 +345,21 @@ protected:
345 UntrainableMetadataTransform() : MetadataTransform(false) {} 345 UntrainableMetadataTransform() : MetadataTransform(false) {}
346 }; 346 };
347 347
  348 +class FileGallery : public Gallery
  349 +{
  350 + Q_OBJECT
  351 +public:
  352 + QFile f;
  353 + qint64 fileSize;
  354 +
  355 + virtual ~FileGallery() { f.close(); }
  356 +
  357 + void init();
  358 +
  359 + qint64 totalSize() { return fileSize; }
  360 + qint64 position() { return f.pos(); }
  361 +};
  362 +
348 } 363 }
349 364
350 #endif // OPENBR_INTERNAL_H 365 #endif // OPENBR_INTERNAL_H
openbr/plugins/process.cpp
@@ -497,6 +497,8 @@ class ProcessWrapperTransform : public TimeVaryingTransform @@ -497,6 +497,8 @@ class ProcessWrapperTransform : public TimeVaryingTransform
497 497
498 void projectUpdate(const TemplateList &src, TemplateList &dst) 498 void projectUpdate(const TemplateList &src, TemplateList &dst)
499 { 499 {
  500 + if (src.empty())
  501 + return;
500 502
501 if (!processActive) 503 if (!processActive)
502 { 504 {