Commit ee6f9bbc83867fb79710659035f4bff6dadc2f10

Authored by Charles Otto
1 parent 8a207670

Preliminary work on not loading complete galleries during enrollment

The basic idea is to read galleries incrementally, but there are some
complications especially related to progress counting--if we don't read a
gallery we don't know how many templates are stored in it since gallery
formats aren't nice enough to provide headers with that information.

One solution to the progress counting problem is to measure progress based
on the position of a file pointer in the gallery file (i.e. measure the
current position in the gallery file, divide by the total size of the
gallery file). This is supported by expanding the Gallery API to include a
totalSize method indicating the total size of the gallery file (or total
number of templates if that is known), and then as templates are read,
their position is stored in metadata (using the "p" key).

Several galleries are updated to respect readBlockSize, and also to store
position data in read templates.

Support for filtering out already enrolled templates in read-mode was
maintained by making the filtering an online process (part of the
enrollment pipeline) rather than a batch process done before
enrollment-proper starts.
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("p", 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("p", 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("p", 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("p", 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("p", 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("p", 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,17 @@ class ProgressCounterTransform : public TimeVaryingTransform @@ -517,14 +517,17 @@ 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>("p",0);
  522 + Globals->currentStep++;
  523 + }
  524 +
520 // updated every second 525 // updated every second
521 if (elapsed > 1000) { 526 if (elapsed > 1000) {
522 Globals->printStatus(); 527 Globals->printStatus();
523 timer.start(); 528 timer.start();
524 } 529 }
525 530
526 - Globals->currentStep++;  
527 -  
528 return; 531 return;
529 } 532 }
530 533
@@ -537,12 +540,13 @@ class ProgressCounterTransform : public TimeVaryingTransform @@ -537,12 +540,13 @@ class ProgressCounterTransform : public TimeVaryingTransform
537 { 540 {
538 (void) data; 541 (void) data;
539 float p = br_progress(); 542 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); 543 + 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 } 544 }
542 545
543 void init() 546 void init()
544 { 547 {
545 timer.start(); 548 timer.start();
  549 + Globals->currentStep = 0;
546 } 550 }
547 551
548 public: 552 public:
@@ -689,6 +693,40 @@ public: @@ -689,6 +693,40 @@ public:
689 693
690 BR_REGISTER(Transform, OutputTransform) 694 BR_REGISTER(Transform, OutputTransform)
691 695
  696 +class FileExclusionTransform : public UntrainableMetaTransform
  697 +{
  698 + Q_OBJECT
  699 +
  700 + Q_PROPERTY(QString exclusionGallery READ get_exclusionGallery WRITE set_exclusionGallery RESET reset_exclusionGallery STORED false)
  701 + BR_PROPERTY(QString, exclusionGallery, "")
  702 +
  703 + QSet<QString> excluded;
  704 +
  705 + void project(const Template & src, Template & dst) const
  706 + {
  707 + qFatal("FileExclusion can't do anything here");
  708 + }
  709 +
  710 + void project(const TemplateList &src, TemplateList &dst) const
  711 + {
  712 + foreach(const Template & srcTemp, src)
  713 + {
  714 + if (!excluded.contains(srcTemp.file))
  715 + dst.append(srcTemp);
  716 + }
  717 + }
  718 +
  719 + void init()
  720 + {
  721 + if (exclusionGallery.isEmpty())
  722 + return;
  723 + FileList temp = FileList::fromGallery(exclusionGallery);
  724 + excluded = QSet<QString>::fromList(temp.names());
  725 + }
  726 +};
  727 +
  728 +BR_REGISTER(Transform, FileExclusionTransform)
  729 +
692 } 730 }
693 731
694 #include "misc.moc" 732 #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 {