Commit 101231ab8f08b28a0ff41e1e65b37c6062fd8d30

Authored by Charles Otto
1 parent 8073b56a

Better support for serializing algorithms in memory

Expand on Object::description to get a workable string that reflects the
current state of the properties of Objects in memory.

With this, we can conceive of serializing Transforms/Distances without
having prior knowledge of the string used to construct them, and also to
serialize them in cases where alterations have been made to their state
in memory.

Also introduce an option to force complete serialization of a transform
tree (i.e. bypass LoadStore blocks), this allows us to do things like
serialize an algorithm in memory, and transmit it to another process
without requiring that the other process read part of the algorithm from
disk.

Refactor ProcessWrapper to directly transmit its current Transform to
child processes, rather than requiring the child processes to read its
state from disk. This allows for far more uniform treatment of
multiProcess and non-multiProcess jobs in AlgorithmCore, leading to
substantial simplification of AlgorithmCore::compare.
openbr/core/core.cpp
@@ -26,21 +26,20 @@ namespace br { @@ -26,21 +26,20 @@ namespace br {
26 struct AlgorithmCore 26 struct AlgorithmCore
27 { 27 {
28 QSharedPointer<Transform> transform; 28 QSharedPointer<Transform> transform;
  29 + QSharedPointer<Transform> comparison;
29 QSharedPointer<Distance> distance; 30 QSharedPointer<Distance> distance;
30 - QString galleryCompareString;  
31 -  
32 - QString transformString;  
33 - QString distanceString; 31 + QSharedPointer<Transform> progressCounter;
34 32
35 AlgorithmCore(const QString &name) 33 AlgorithmCore(const QString &name)
36 { 34 {
37 this->name = name; 35 this->name = name;
38 init(name); 36 init(name);
  37 + progressCounter = QSharedPointer<Transform>(Transform::make("ProgressCounter", NULL));
39 } 38 }
40 39
41 bool isClassifier() const 40 bool isClassifier() const
42 { 41 {
43 - return distance.isNull(); 42 + return comparison.isNull();
44 } 43 }
45 44
46 void train(const File &input, const QString &model) 45 void train(const File &input, const QString &model)
@@ -116,7 +115,9 @@ struct AlgorithmCore @@ -116,7 +115,9 @@ struct AlgorithmCore
116 in >> name; init(Globals->abbreviations.contains(name) ? Globals->abbreviations[name] : name); 115 in >> name; init(Globals->abbreviations.contains(name) ? Globals->abbreviations[name] : name);
117 transform->load(in); 116 transform->load(in);
118 bool hasDistance; in >> hasDistance; 117 bool hasDistance; in >> hasDistance;
119 - if (hasDistance) distance->load(in); 118 +
  119 + if (hasDistance)
  120 + distance->load(in);
120 } 121 }
121 122
122 File getMemoryGallery(const File &file) const 123 File getMemoryGallery(const File &file) const
@@ -148,54 +149,37 @@ struct AlgorithmCore @@ -148,54 +149,37 @@ struct AlgorithmCore
148 Gallery *temp = Gallery::make(input); 149 Gallery *temp = Gallery::make(input);
149 qint64 total = temp->totalSize(); 150 qint64 total = temp->totalSize();
150 151
151 - QScopedPointer<Transform> basePipe;  
152 -  
153 - QString pipeDesc = "GalleryOutput("+gallery.flat()+")+ProgressCounter("+QString::number(total)+")+Discard";  
154 -  
155 - if (!multiProcess) {  
156 - basePipe.reset(Transform::make(pipeDesc,NULL));  
157 - CompositeTransform *downcast = dynamic_cast<CompositeTransform *>(basePipe.data());  
158 -  
159 - if (downcast == NULL) qFatal("downcast failed?"); 152 + Transform * enroll = this->transform.data();
  153 + if (multiProcess)
  154 + enroll = wrapTransform(enroll, "ProcessWrapper");
160 155
161 - downcast->transforms.prepend(this->transform.data());  
162 - if (fileExclusion) {  
163 - Transform *temp = Transform::make("FileExclusion(" + gallery.flat() + ")", downcast);  
164 - downcast->transforms.prepend(temp);  
165 - }  
166 -  
167 - // call init on the pipe to collapse the algorithm (if its top level is a pipe)  
168 - downcast->init();  
169 - }  
170 - else {  
171 - pipeDesc = "ProcessWrapper("+transformString+")+"+pipeDesc;  
172 - if (fileExclusion)  
173 - pipeDesc = "FileExclusion(" + gallery.flat() +")+" + pipeDesc; 156 + QList<Transform *> stages;
  157 + stages.append(enroll);
174 158
175 - basePipe.reset(Transform::make(pipeDesc,NULL));  
176 - } 159 + QString outputDesc;
  160 + if (fileExclusion)
  161 + outputDesc = "FileExclusion(" + gallery.flat() + ")+";
  162 + outputDesc.append("GalleryOutput("+gallery.flat()+")");
  163 + QScopedPointer<Transform> outputTform(Transform::make(outputDesc, NULL));
  164 + stages.append(outputTform.data());
  165 + stages.append(progressCounter.data());
  166 + QScopedPointer<Transform> discard(Transform::make("Discard",NULL));
  167 + stages.append(discard.data());
177 168
178 - // Next, we make a Stream (with placeholder transform)  
179 - QString streamDesc = "Stream(readMode=StreamGallery)";  
180 - QScopedPointer<Transform> baseStream(Transform::make(streamDesc, NULL));  
181 - WrapperTransform *wrapper = dynamic_cast<WrapperTransform *> (baseStream.data()); 169 + QScopedPointer<Transform> pipeline(br::pipeTransforms(stages));
182 170
183 - // replace that placeholder with the pipe we built  
184 - wrapper->transform = basePipe.data();  
185 -  
186 - // and get the final stream's stages by reinterpreting the pipe. Perfectly straightforward.  
187 - wrapper->init();  
188 -  
189 - Globals->startTime.start();  
190 - Globals->currentStep = 0;  
191 - Globals->totalSteps = total; 171 + QScopedPointer<Transform> stream(br::wrapTransform(pipeline.data(), "Stream(readMode=StreamGallery)"));
192 172
193 TemplateList data, output; 173 TemplateList data, output;
194 data.append(input); 174 data.append(input);
195 - wrapper->projectUpdate(data, output); 175 + progressCounter->setPropertyRecursive("totalProgress", QString::number(total));
  176 + stream->projectUpdate(data, output);
196 177
197 files.append(output.files()); 178 files.append(output.files());
198 179
  180 + if (multiProcess)
  181 + delete enroll;
  182 +
199 return files; 183 return files;
200 } 184 }
201 185
@@ -389,12 +373,12 @@ struct AlgorithmCore @@ -389,12 +373,12 @@ struct AlgorithmCore
389 // simple make sure the enrolled data is stored in a memGallery, but in multi-process mode we save the enrolled 373 // simple make sure the enrolled data is stored in a memGallery, but in multi-process mode we save the enrolled
390 // data to disk (as a .gal file) so that each worker process can read it without re-doing enrollment. 374 // data to disk (as a .gal file) so that each worker process can read it without re-doing enrollment.
391 File colEnrolledGallery = colGallery; 375 File colEnrolledGallery = colGallery;
392 - QString targetExtension = multiProcess ? "gal" : "mem"; 376 + QString targetExtension = "mem";
393 377
394 // If the column gallery is not already of the appropriate type, we need to do something 378 // If the column gallery is not already of the appropriate type, we need to do something
395 if (colGallery.suffix() != targetExtension) { 379 if (colGallery.suffix() != targetExtension) {
396 // Build the name of a gallery containing the enrolled data, of the appropriate type. 380 // Build the name of a gallery containing the enrolled data, of the appropriate type.
397 - colEnrolledGallery = colGallery.baseName() + colGallery.hash() + (multiProcess ? ".gal" : ".mem"); 381 + colEnrolledGallery = colGallery.baseName() + colGallery.hash() + '.' + targetExtension;
398 382
399 // Check if we have to do real enrollment, and not just convert the gallery's type. 383 // Check if we have to do real enrollment, and not just convert the gallery's type.
400 if (!(QStringList() << "gal" << "template" << "mem").contains(colGallery.suffix())) 384 if (!(QStringList() << "gal" << "template" << "mem").contains(colGallery.suffix()))
@@ -435,37 +419,23 @@ struct AlgorithmCore @@ -435,37 +419,23 @@ struct AlgorithmCore
435 // The actual comparison step is done by a GalleryCompare transform, which has a Distance, and a gallery as data. 419 // The actual comparison step is done by a GalleryCompare transform, which has a Distance, and a gallery as data.
436 // Incoming templates are compared against the templates in the gallery, and the output is the resulting score 420 // Incoming templates are compared against the templates in the gallery, and the output is the resulting score
437 // vector. 421 // vector.
  422 + TemplateList tlist = TemplateList::fromGallery(colEnrolledGallery);
  423 + comparison->train(tlist);
  424 + comparison->setPropertyRecursive("galleryName","");
  425 +
438 QString compareRegionDesc; 426 QString compareRegionDesc;
  427 + QList<Transform *> enrollCompare;
  428 + enrollCompare.append(comparison.data());
  429 + // if we have to enroll the row gallery, add that transform to the list
  430 + if (needEnrollRows)
  431 + enrollCompare.prepend(this->transform.data());
439 432
440 - if (this->galleryCompareString.isEmpty() )  
441 - compareRegionDesc = "Pipe([GalleryCompare("+Globals->algorithm+",galleryName="+colEnrolledGallery.flat()+")])";  
442 - else  
443 - compareRegionDesc = "Pipe(["+galleryCompareString+"(galleryName="+colEnrolledGallery.flat()+")])";  
444 -  
445 - QScopedPointer<Transform> compareRegion;  
446 - // If we need to enroll the row set, we add the current algorithm's enrollment transform before the  
447 - // GalleryCompare in a pipe.  
448 - if (needEnrollRows) {  
449 - if (!multiProcess) {  
450 - compareRegionDesc = compareRegionDesc;  
451 - compareRegion.reset(Transform::make(compareRegionDesc,NULL));  
452 - CompositeTransform *downcast = dynamic_cast<CompositeTransform *> (compareRegion.data());  
453 - if (downcast == NULL)  
454 - qFatal("Pipe downcast failed in compare");  
455 -  
456 - downcast->transforms.prepend(this->transform.data());  
457 - downcast->init();  
458 - }  
459 - else {  
460 - compareRegionDesc = "ProcessWrapper(" + this->transformString + "+" + compareRegionDesc + ")";  
461 - compareRegion.reset(Transform::make(compareRegionDesc, NULL));  
462 - }  
463 - }  
464 - else {  
465 - if (multiProcess)  
466 - compareRegionDesc = "ProcessWrapper(" + compareRegionDesc + ")";  
467 - compareRegion.reset(Transform::make(compareRegionDesc,NULL));  
468 - } 433 + Transform * compareRegionBase = pipeTransforms(enrollCompare);
  434 + // If in multi-process mode, wrap the enroll+compare structure in a ProcessWrapper.
  435 + if (multiProcess)
  436 + compareRegionBase = wrapTransform(compareRegionBase, "ProcessWrapper");
  437 +
  438 + QScopedPointer<Transform> compareRegion(compareRegionBase);
469 439
470 // At this point, compareRegion is a transform, which optionally does enrollment, then compares the row 440 // At this point, compareRegion is a transform, which optionally does enrollment, then compares the row
471 // set against the column set. If in multi-process mode, the enrollment and comparison are wrapped in a 441 // set against the column set. If in multi-process mode, the enrollment and comparison are wrapped in a
@@ -473,47 +443,36 @@ struct AlgorithmCore @@ -473,47 +443,36 @@ struct AlgorithmCore
473 443
474 // We also need to add Output and progress counting to the algorithm we are building, so we will assign them to 444 // We also need to add Output and progress counting to the algorithm we are building, so we will assign them to
475 // two stages of a pipe. 445 // two stages of a pipe.
476 - QString joinDesc = "Pipe()";  
477 - QScopedPointer<Transform> join(Transform::make(joinDesc, NULL)); 446 + QList<Transform *> compareOutput;
  447 + compareOutput.append(compareRegion.data());
478 448
479 // The output transform takes the metadata memGalleries we set up previously as input, along with the 449 // The output transform takes the metadata memGalleries we set up previously as input, along with the
480 // output specification we were passed. Gallery metadata is necessary for some Outputs to function correctly. 450 // output specification we were passed. Gallery metadata is necessary for some Outputs to function correctly.
481 QString outputString = output.flat().isEmpty() ? "Empty" : output.flat(); 451 QString outputString = output.flat().isEmpty() ? "Empty" : output.flat();
482 QString outputRegionDesc = "Output("+ outputString +"," + targetGallery.flat() +"," + queryGallery.flat() + ","+ QString::number(transposeMode ? 1 : 0) + ")"; 452 QString outputRegionDesc = "Output("+ outputString +"," + targetGallery.flat() +"," + queryGallery.flat() + ","+ QString::number(transposeMode ? 1 : 0) + ")";
483 - // The ProgressCounter transform will simply provide a display about the number of rows completed.  
484 - outputRegionDesc += "+ProgressCounter("+QString::number(rowSize)+")+Discard";  
485 - QScopedPointer<Transform> outputTform(Transform::make(outputRegionDesc, NULL)); 453 + QScopedPointer<Transform> outputTForm(Transform::make(outputRegionDesc,NULL));
  454 + compareOutput.append(outputTForm.data());
486 455
487 - // Assign the comparison transform we previously built, and the output transform we just built to  
488 - // two stages of a pipe.  
489 - CompositeTransform *downcast = dynamic_cast<CompositeTransform *> (join.data());  
490 - downcast->transforms.append(compareRegion.data());  
491 - downcast->transforms.append(outputTform.data()); 456 + // The ProgressCounter transform will simply provide a display about the number of rows completed.
  457 + compareOutput.append(progressCounter.data());
  458 + QScopedPointer<Transform> discard(Transform::make("Discard",NULL));
  459 + compareOutput.append(discard.data());
492 460
493 // With this, we have set up a transform which (optionally) enrolls templates, compares them 461 // With this, we have set up a transform which (optionally) enrolls templates, compares them
494 // against a gallery, and outputs them. 462 // against a gallery, and outputs them.
495 - join->init(); 463 + Transform * pipeline = br::pipeTransforms(compareOutput);
496 464
497 // Now, we will give that base transform to a stream, which will incrementally read the row gallery 465 // Now, we will give that base transform to a stream, which will incrementally read the row gallery
498 // and pass the transforms it reads through the base algorithm. 466 // and pass the transforms it reads through the base algorithm.
499 - QString streamDesc = "Stream(readMode=StreamGallery)";  
500 - QScopedPointer<Transform> streamBase(Transform::make(streamDesc, NULL));  
501 - WrapperTransform *streamWrapper = dynamic_cast<WrapperTransform *> (streamBase.data());  
502 - streamWrapper->transform = join.data();  
503 -  
504 - // The transform we will use is now complete.  
505 - streamWrapper->init(); 467 + QScopedPointer<Transform> streamWrapper(br::wrapTransform(pipeline, "Stream(readMode=StreamGallery)"));
506 468
507 // We set up a template containing the rowGallery we want to compare. 469 // We set up a template containing the rowGallery we want to compare.
508 TemplateList rowGalleryTemplate; 470 TemplateList rowGalleryTemplate;
509 rowGalleryTemplate.append(Template(rowGallery)); 471 rowGalleryTemplate.append(Template(rowGallery));
510 TemplateList outputGallery; 472 TemplateList outputGallery;
511 473
512 - // Set up progress counting variables  
513 - Globals->currentStep = 0;  
514 - Globals->currentProgress = 0;  
515 - Globals->totalSteps = rowSize;  
516 - Globals->startTime.start(); 474 + // initialize the progress counter
  475 + progressCounter->setPropertyRecursive("totalProgress", QString::number(rowSize));
517 476
518 // Do the actual comparisons 477 // Do the actual comparisons
519 streamWrapper->projectUpdate(rowGalleryTemplate, outputGallery); 478 streamWrapper->projectUpdate(rowGalleryTemplate, outputGallery);
@@ -557,27 +516,22 @@ private: @@ -557,27 +516,22 @@ private:
557 return; 516 return;
558 } 517 }
559 518
  519 + //! [Parsing the algorithm description]
560 const bool compareTransform = description.contains('!'); 520 const bool compareTransform = description.contains('!');
561 QStringList words = QtUtils::parse(description, compareTransform ? '!' : ':'); 521 QStringList words = QtUtils::parse(description, compareTransform ? '!' : ':');
562 522
563 if ((words.size() < 1) || (words.size() > 2)) qFatal("Invalid algorithm format."); 523 if ((words.size() < 1) || (words.size() > 2)) qFatal("Invalid algorithm format.");
564 524
565 - //! [Parsing the algorithm description]  
566 - transformString = words[0];  
567 -  
568 //! [Creating the template generation and comparison methods] 525 //! [Creating the template generation and comparison methods]
569 transform = QSharedPointer<Transform>(Transform::make(words[0], NULL)); 526 transform = QSharedPointer<Transform>(Transform::make(words[0], NULL));
570 if (words.size() > 1) { 527 if (words.size() > 1) {
571 if (!compareTransform) { 528 if (!compareTransform) {
572 distance = QSharedPointer<Distance>(Distance::make(words[1], NULL)); 529 distance = QSharedPointer<Distance>(Distance::make(words[1], NULL));
573 - distanceString = words[1];  
574 - galleryCompareString.clear();  
575 - }  
576 - else {  
577 - galleryCompareString = words[1];  
578 - distanceString.clear(); 530 + comparison = QSharedPointer<Transform>(Transform::make("GalleryCompare", NULL));
  531 + comparison->setPropertyRecursive("distance", QVariant::fromValue(distance.data()));
579 } 532 }
580 - 533 + else
  534 + comparison = QSharedPointer<Transform>(Transform::make(words[1], NULL));
581 } 535 }
582 //! [Creating the template generation and comparison methods] 536 //! [Creating the template generation and comparison methods]
583 } 537 }
openbr/openbr_plugin.cpp
@@ -577,16 +577,31 @@ QStringList Object::parameters() const @@ -577,16 +577,31 @@ QStringList Object::parameters() const
577 return parameters; 577 return parameters;
578 } 578 }
579 579
580 -QStringList Object::arguments() const 580 +QStringList Object::arguments(bool expanded)
581 { 581 {
582 QStringList arguments; 582 QStringList arguments;
583 - for (int i=metaObject()->propertyOffset(); i<metaObject()->propertyCount(); i++)  
584 - arguments.append(argument(i)); 583 +
  584 + for (int i=firstAvailablePropertyIdx; i<metaObject()->propertyCount(); i++) {
  585 + const char *name = metaObject()->property(i).name();
  586 +
  587 + QVariant oldVal = property(name);
  588 + QVariant defaultVal = oldVal;
  589 +
  590 + if (metaObject()->property(i).isResettable()) {
  591 + metaObject()->property(i).reset(this);
  592 + defaultVal = property(name);
  593 + }
  594 +
  595 + if (defaultVal != oldVal) {
  596 + metaObject()->property(i).write(this, oldVal);
  597 + arguments.append(name + QString("=") + argument(i, expanded));
  598 + }
  599 + }
585 600
586 return arguments; 601 return arguments;
587 } 602 }
588 603
589 -QString Object::argument(int index) const 604 +QString Object::argument(int index, bool expanded) const
590 { 605 {
591 if ((index < 0) || (index > metaObject()->propertyCount())) return ""; 606 if ((index < 0) || (index > metaObject()->propertyCount())) return "";
592 const QMetaProperty property = metaObject()->property(index); 607 const QMetaProperty property = metaObject()->property(index);
@@ -604,19 +619,19 @@ QString Object::argument(int index) const @@ -604,19 +619,19 @@ QString Object::argument(int index) const
604 strings.append(QString::number(value)); 619 strings.append(QString::number(value));
605 } else if (type == "QList<br::Transform*>") { 620 } else if (type == "QList<br::Transform*>") {
606 foreach (Transform *transform, variant.value< QList<Transform*> >()) 621 foreach (Transform *transform, variant.value< QList<Transform*> >())
607 - strings.append(transform->description()); 622 + strings.append(transform->description(expanded));
608 } else if (type == "QList<br::Distance*>") { 623 } else if (type == "QList<br::Distance*>") {
609 foreach (Distance *distance, variant.value< QList<Distance*> >()) 624 foreach (Distance *distance, variant.value< QList<Distance*> >())
610 - strings.append(distance->description()); 625 + strings.append(distance->description(expanded));
611 } else { 626 } else {
612 qFatal("Unrecognized type: %s", qPrintable(type)); 627 qFatal("Unrecognized type: %s", qPrintable(type));
613 } 628 }
614 629
615 return "[" + strings.join(",") + "]"; 630 return "[" + strings.join(",") + "]";
616 } else if (type == "br::Transform*") { 631 } else if (type == "br::Transform*") {
617 - return variant.value<Transform*>()->description(); 632 + return variant.value<Transform*>()->description(expanded);
618 } else if (type == "br::Distance*") { 633 } else if (type == "br::Distance*") {
619 - return variant.value<Distance*>()->description(); 634 + return variant.value<Distance*>()->description(expanded);
620 } else if (type == "QStringList") { 635 } else if (type == "QStringList") {
621 return "[" + variant.toStringList().join(",") + "]"; 636 return "[" + variant.toStringList().join(",") + "]";
622 } 637 }
@@ -624,13 +639,17 @@ QString Object::argument(int index) const @@ -624,13 +639,17 @@ QString Object::argument(int index) const
624 return variant.toString(); 639 return variant.toString();
625 } 640 }
626 641
627 -QString Object::description() const 642 +QString Object::description(bool expanded)
628 { 643 {
629 - QString argumentString = arguments().join(","); 644 + (void) expanded;
  645 + QString argumentString = arguments(expanded).join(",");
  646 + if (argumentString.endsWith(","))
  647 + argumentString.chop(1);
  648 +
630 return objectName() + (argumentString.isEmpty() ? "" : ("(" + argumentString + ")")); 649 return objectName() + (argumentString.isEmpty() ? "" : ("(" + argumentString + ")"));
631 } 650 }
632 651
633 -void Object::store(QDataStream &stream) const 652 +void Object::store(QDataStream &stream, bool force) const
634 { 653 {
635 // Start from 1 to skip QObject::objectName 654 // Start from 1 to skip QObject::objectName
636 for (int i=1; i<metaObject()->propertyCount(); i++) { 655 for (int i=1; i<metaObject()->propertyCount(); i++) {
@@ -641,14 +660,14 @@ void Object::store(QDataStream &amp;stream) const @@ -641,14 +660,14 @@ void Object::store(QDataStream &amp;stream) const
641 const QString type = property.typeName(); 660 const QString type = property.typeName();
642 if (type == "QList<br::Transform*>") { 661 if (type == "QList<br::Transform*>") {
643 foreach (Transform *transform, property.read(this).value< QList<Transform*> >()) 662 foreach (Transform *transform, property.read(this).value< QList<Transform*> >())
644 - transform->store(stream); 663 + transform->store(stream, force);
645 } else if (type == "QList<br::Distance*>") { 664 } else if (type == "QList<br::Distance*>") {
646 foreach (Distance *distance, property.read(this).value< QList<Distance*> >()) 665 foreach (Distance *distance, property.read(this).value< QList<Distance*> >())
647 - distance->store(stream); 666 + distance->store(stream, force);
648 } else if (type == "br::Transform*") { 667 } else if (type == "br::Transform*") {
649 - property.read(this).value<Transform*>()->store(stream); 668 + property.read(this).value<Transform*>()->store(stream, force);
650 } else if (type == "br::Distance*") { 669 } else if (type == "br::Distance*") {
651 - property.read(this).value<Distance*>()->store(stream); 670 + property.read(this).value<Distance*>()->store(stream, force);
652 } else if (type == "bool") { 671 } else if (type == "bool") {
653 stream << property.read(this).toBool(); 672 stream << property.read(this).toBool();
654 } else if (type == "int") { 673 } else if (type == "int") {
@@ -1284,7 +1303,8 @@ Transform *Transform::make(QString str, QObject *parent) @@ -1284,7 +1303,8 @@ Transform *Transform::make(QString str, QObject *parent)
1284 1303
1285 Transform *Transform::clone() const 1304 Transform *Transform::clone() const
1286 { 1305 {
1287 - Transform *clone = Factory<Transform>::make(file.flat()); 1306 + Transform * temp = (Transform *) this;
  1307 + Transform *clone = Factory<Transform>::make("."+temp->description(false));
1288 return clone; 1308 return clone;
1289 } 1309 }
1290 1310
@@ -1453,3 +1473,17 @@ void br::applyAdditionalProperties(const File &amp;temp, Transform *target) @@ -1453,3 +1473,17 @@ void br::applyAdditionalProperties(const File &amp;temp, Transform *target)
1453 target->setPropertyRecursive(i.key(), i.value() ); 1473 target->setPropertyRecursive(i.key(), i.value() );
1454 } 1474 }
1455 } 1475 }
  1476 +
  1477 +Transform *br::wrapTransform(Transform *base, const QString &target)
  1478 +{
  1479 + Transform *res = Transform::make(target, NULL);
  1480 + res->setPropertyRecursive("transform", QVariant::fromValue(base));
  1481 + return res;
  1482 +}
  1483 +
  1484 +Transform *br::pipeTransforms(QList<Transform *> &transforms)
  1485 +{
  1486 + Transform *res = Transform::make("Pipe",NULL);
  1487 + res->setPropertyRecursive("transforms", QVariant::fromValue(transforms));
  1488 + return res;
  1489 +}
openbr/openbr_plugin.h
@@ -589,13 +589,20 @@ public: @@ -589,13 +589,20 @@ public:
589 File file; /*!< \brief The file used to construct the plugin. */ 589 File file; /*!< \brief The file used to construct the plugin. */
590 590
591 virtual void init() {} /*!< \brief Overload this function instead of the default constructor to initialize the derived class. It should be safe to call this function multiple times. */ 591 virtual void init() {} /*!< \brief Overload this function instead of the default constructor to initialize the derived class. It should be safe to call this function multiple times. */
592 - virtual void store(QDataStream &stream) const; /*!< \brief Serialize the object. */ 592 + virtual void store(QDataStream &stream, bool force = false) const; /*!< \brief Serialize the object. */
593 virtual void load(QDataStream &stream); /*!< \brief Deserialize the object. Default implementation calls init() after deserialization. */ 593 virtual void load(QDataStream &stream); /*!< \brief Deserialize the object. Default implementation calls init() after deserialization. */
594 594
  595 + virtual void serialize(QDataStream & stream, bool force)
  596 + {
  597 + stream << description(force);
  598 + store(stream, force);
  599 + }
  600 +
595 QStringList parameters() const; /*!< \brief A string describing the parameters the object takes. */ 601 QStringList parameters() const; /*!< \brief A string describing the parameters the object takes. */
596 - QStringList arguments() const; /*!< \brief A string describing the values the object has. */  
597 - QString argument(int index) const; /*!< \brief A string value for the argument at the specified index. */  
598 - QString description() const; /*!< \brief Returns a string description of the object. */ 602 + QStringList arguments(bool expanded = false); /*!< \brief A string describing the values the object has. */
  603 + QString argument(int index, bool expanded) const; /*!< \brief A string value for the argument at the specified index. */
  604 + virtual QString description(bool expanded = false); /*!< \brief Returns a string description of the object. */
  605 +
599 void setProperty(const QString &name, QVariant value); /*!< \brief Overload of QObject::setProperty to handle OpenBR data types. */ 606 void setProperty(const QString &name, QVariant value); /*!< \brief Overload of QObject::setProperty to handle OpenBR data types. */
600 virtual bool setPropertyRecursive(const QString &name, QVariant value); /*!< \brief Recursive version of setProperty, try to set the property on this object, or its children, returns true if successful. */ 607 virtual bool setPropertyRecursive(const QString &name, QVariant value); /*!< \brief Recursive version of setProperty, try to set the property on this object, or its children, returns true if successful. */
601 608
@@ -1273,6 +1280,15 @@ public: @@ -1273,6 +1280,15 @@ public:
1273 */ 1280 */
1274 QList<Transform *> getChildren() const; 1281 QList<Transform *> getChildren() const;
1275 1282
  1283 + static Transform *deserialize(QDataStream &stream)
  1284 + {
  1285 + QString desc;
  1286 + stream >> desc;
  1287 + Transform *res = Transform::make(desc, NULL);
  1288 + res->load(stream);
  1289 + return res;
  1290 + }
  1291 +
1276 protected: 1292 protected:
1277 Transform(bool independent = true, bool trainable = true); /*!< \brief Construct a transform. */ 1293 Transform(bool independent = true, bool trainable = true); /*!< \brief Construct a transform. */
1278 inline Transform *make(const QString &description) { return make(description, this); } /*!< \brief Make a subtransform. */ 1294 inline Transform *make(const QString &description) { return make(description, this); } /*!< \brief Make a subtransform. */
openbr/plugins/cascade.cpp
@@ -415,7 +415,7 @@ class CascadeTransform : public MetaTransform @@ -415,7 +415,7 @@ class CascadeTransform : public MetaTransform
415 } 415 }
416 416
417 // TODO: Remove this code when ready to break binary compatibility 417 // TODO: Remove this code when ready to break binary compatibility
418 - void store(QDataStream &stream) const 418 + void store(QDataStream &stream, bool force) const
419 { 419 {
420 int size = 1; 420 int size = 1;
421 stream << size; 421 stream << size;
openbr/plugins/cluster.cpp
@@ -69,7 +69,7 @@ class KMeansTransform : public Transform @@ -69,7 +69,7 @@ class KMeansTransform : public Transform
69 reindex(); 69 reindex();
70 } 70 }
71 71
72 - void store(QDataStream &stream) const 72 + void store(QDataStream &stream, bool force) const
73 { 73 {
74 stream << centers; 74 stream << centers;
75 } 75 }
@@ -131,7 +131,7 @@ class KNNTransform : public Transform @@ -131,7 +131,7 @@ class KNNTransform : public Transform
131 dst.file.set("Nearest", gallery[sortedScores[0].second].file.name); 131 dst.file.set("Nearest", gallery[sortedScores[0].second].file.name);
132 } 132 }
133 133
134 - void store(QDataStream &stream) const 134 + void store(QDataStream &stream, bool force) const
135 { 135 {
136 stream << gallery; 136 stream << gallery;
137 } 137 }
@@ -196,7 +196,7 @@ class RandomCentroidsTransform : public Transform @@ -196,7 +196,7 @@ class RandomCentroidsTransform : public Transform
196 reindex(); 196 reindex();
197 } 197 }
198 198
199 - void store(QDataStream &stream) const 199 + void store(QDataStream &stream, bool force) const
200 { 200 {
201 stream << centers; 201 stream << centers;
202 } 202 }
openbr/plugins/distance.cpp
@@ -253,11 +253,11 @@ private: @@ -253,11 +253,11 @@ private:
253 } 253 }
254 } 254 }
255 255
256 - void store(QDataStream &stream) const 256 + void store(QDataStream &stream, bool force) const
257 { 257 {
258 stream << distances.size(); 258 stream << distances.size();
259 foreach (Distance *distance, distances) 259 foreach (Distance *distance, distances)
260 - distance->store(stream); 260 + distance->store(stream, force);
261 } 261 }
262 262
263 void load(QDataStream &stream) 263 void load(QDataStream &stream)
@@ -328,9 +328,9 @@ class NegativeLogPlusOneDistance : public Distance @@ -328,9 +328,9 @@ class NegativeLogPlusOneDistance : public Distance
328 return -log(distance->compare(a,b)+1); 328 return -log(distance->compare(a,b)+1);
329 } 329 }
330 330
331 - void store(QDataStream &stream) const 331 + void store(QDataStream &stream, bool force) const
332 { 332 {
333 - distance->store(stream); 333 + distance->store(stream, force);
334 } 334 }
335 335
336 void load(QDataStream &stream) 336 void load(QDataStream &stream)
@@ -460,13 +460,12 @@ BR_REGISTER(Distance, SumDistance) @@ -460,13 +460,12 @@ BR_REGISTER(Distance, SumDistance)
460 class GalleryCompareTransform : public Transform 460 class GalleryCompareTransform : public Transform
461 { 461 {
462 Q_OBJECT 462 Q_OBJECT
463 - Q_PROPERTY(QString distanceAlgorithm READ get_distanceAlgorithm WRITE set_distanceAlgorithm RESET reset_distanceAlgorithm STORED false) 463 + Q_PROPERTY(br::Distance *distance READ get_distance WRITE set_distance RESET reset_distance STORED true)
464 Q_PROPERTY(QString galleryName READ get_galleryName WRITE set_galleryName RESET reset_galleryName STORED false) 464 Q_PROPERTY(QString galleryName READ get_galleryName WRITE set_galleryName RESET reset_galleryName STORED false)
465 - BR_PROPERTY(QString, distanceAlgorithm, "") 465 + BR_PROPERTY(br::Distance*, distance, NULL)
466 BR_PROPERTY(QString, galleryName, "") 466 BR_PROPERTY(QString, galleryName, "")
467 467
468 TemplateList gallery; 468 TemplateList gallery;
469 - QSharedPointer<Distance> distance;  
470 469
471 void project(const Template &src, Template &dst) const 470 void project(const Template &src, Template &dst) const
472 { 471 {
@@ -480,16 +479,29 @@ class GalleryCompareTransform : public Transform @@ -480,16 +479,29 @@ class GalleryCompareTransform : public Transform
480 479
481 void init() 480 void init()
482 { 481 {
483 - if (!galleryName.isEmpty()) { 482 + if (!galleryName.isEmpty())
484 gallery = TemplateList::fromGallery(galleryName); 483 gallery = TemplateList::fromGallery(galleryName);
485 - }  
486 - if (!distanceAlgorithm.isEmpty())  
487 - {  
488 - distance = Distance::fromAlgorithm(distanceAlgorithm);  
489 - }  
490 } 484 }
  485 +
  486 + void train(const TemplateList & data)
  487 + {
  488 + gallery = data;
  489 + }
  490 +
  491 + void store(QDataStream &stream, bool force) const
  492 + {
  493 + br::Object::store(stream, force);
  494 + stream << gallery;
  495 + }
  496 +
  497 + void load(QDataStream &stream)
  498 + {
  499 + br::Object::load(stream);
  500 + stream >> gallery;
  501 + }
  502 +
491 public: 503 public:
492 - GalleryCompareTransform() : Transform(false, false) {} 504 + GalleryCompareTransform() : Transform(false, true) {}
493 }; 505 };
494 506
495 BR_REGISTER(Transform, GalleryCompareTransform) 507 BR_REGISTER(Transform, GalleryCompareTransform)
openbr/plugins/eigen3.cpp
@@ -119,7 +119,7 @@ private: @@ -119,7 +119,7 @@ private:
119 outMap = eVecs.transpose() * (inMap - mean); 119 outMap = eVecs.transpose() * (inMap - mean);
120 } 120 }
121 121
122 - void store(QDataStream &stream) const 122 + void store(QDataStream &stream, bool force) const
123 { 123 {
124 stream << keep << drop << whiten << originalRows << mean << eVals << eVecs; 124 stream << keep << drop << whiten << originalRows << mean << eVals << eVecs;
125 } 125 }
@@ -295,9 +295,9 @@ class DFFSTransform : public Transform @@ -295,9 +295,9 @@ class DFFSTransform : public Transform
295 dst.file.set("DFFS", sqrt(pca.residualReconstructionError((*cvtFloat)(src)))); 295 dst.file.set("DFFS", sqrt(pca.residualReconstructionError((*cvtFloat)(src))));
296 } 296 }
297 297
298 - void store(QDataStream &stream) const 298 + void store(QDataStream &stream, bool force) const
299 { 299 {
300 - pca.store(stream); 300 + pca.store(stream, force);
301 } 301 }
302 302
303 void load(QDataStream &stream) 303 void load(QDataStream &stream)
@@ -525,7 +525,7 @@ class LDATransform : public Transform @@ -525,7 +525,7 @@ class LDATransform : public Transform
525 dst.m().at<float>(0,0) = dst.m().at<float>(0,0) / stdDev; 525 dst.m().at<float>(0,0) = dst.m().at<float>(0,0) / stdDev;
526 } 526 }
527 527
528 - void store(QDataStream &stream) const 528 + void store(QDataStream &stream, bool force) const
529 { 529 {
530 stream << pcaKeep; 530 stream << pcaKeep;
531 stream << directLDA; 531 stream << directLDA;
@@ -631,10 +631,10 @@ class SparseLDATransform : public Transform @@ -631,10 +631,10 @@ class SparseLDATransform : public Transform
631 ldaSparse.project(Template(src.file, inSelect), dst); 631 ldaSparse.project(Template(src.file, inSelect), dst);
632 } 632 }
633 633
634 - void store(QDataStream &stream) const 634 + void store(QDataStream &stream, bool force) const
635 { 635 {
636 stream << pcaKeep; 636 stream << pcaKeep;
637 - stream << ldaSparse; 637 + ldaSparse.store(stream, force);
638 stream << dimsOut; 638 stream << dimsOut;
639 stream << selections; 639 stream << selections;
640 } 640 }
openbr/plugins/frames.cpp
@@ -43,7 +43,7 @@ private: @@ -43,7 +43,7 @@ private:
43 buffer.clear(); 43 buffer.clear();
44 } 44 }
45 45
46 - void store(QDataStream &stream) const 46 + void store(QDataStream &stream, bool force) const
47 { 47 {
48 (void) stream; 48 (void) stream;
49 } 49 }
openbr/plugins/independent.cpp
@@ -126,6 +126,10 @@ class IndependentTransform : public MetaTransform @@ -126,6 +126,10 @@ class IndependentTransform : public MetaTransform
126 126
127 QList<Transform*> transforms; 127 QList<Transform*> transforms;
128 128
  129 + QString description(bool expanded)
  130 + {
  131 + return transform->description(expanded);
  132 + }
129 133
130 bool setPropertyRecursive(const QString &name, QVariant value) 134 bool setPropertyRecursive(const QString &name, QVariant value)
131 { 135 {
@@ -226,12 +230,12 @@ class IndependentTransform : public MetaTransform @@ -226,12 +230,12 @@ class IndependentTransform : public MetaTransform
226 } 230 }
227 } 231 }
228 232
229 - void store(QDataStream &stream) const 233 + void store(QDataStream &stream, bool force) const
230 { 234 {
231 const int size = transforms.size(); 235 const int size = transforms.size();
232 stream << size; 236 stream << size;
233 for (int i=0; i<size; i++) 237 for (int i=0; i<size; i++)
234 - transforms[i]->store(stream); 238 + transforms[i]->store(stream, force);
235 } 239 }
236 240
237 void load(QDataStream &stream) 241 void load(QDataStream &stream)
@@ -292,10 +296,10 @@ class SingletonTransform : public MetaTransform @@ -292,10 +296,10 @@ class SingletonTransform : public MetaTransform
292 transform->project(src, dst); 296 transform->project(src, dst);
293 } 297 }
294 298
295 - void store(QDataStream &stream) const 299 + void store(QDataStream &stream, bool force) const
296 { 300 {
297 if (transform->parent() == this) 301 if (transform->parent() == this)
298 - transform->store(stream); 302 + transform->store(stream, force);
299 } 303 }
300 304
301 void load(QDataStream &stream) 305 void load(QDataStream &stream)
openbr/plugins/integral.cpp
@@ -251,12 +251,12 @@ class RecursiveIntegralSamplerTransform : public Transform @@ -251,12 +251,12 @@ class RecursiveIntegralSamplerTransform : public Transform
251 } 251 }
252 } 252 }
253 253
254 - void store(QDataStream &stream) const 254 + void store(QDataStream &stream, bool force) const
255 { 255 {
256 - transform->store(stream); 256 + transform->store(stream, force);
257 stream << (subTransform != NULL); 257 stream << (subTransform != NULL);
258 if (subTransform != NULL) 258 if (subTransform != NULL)
259 - subTransform->store(stream); 259 + subTransform->store(stream, force);
260 } 260 }
261 261
262 void load(QDataStream &stream) 262 void load(QDataStream &stream)
openbr/plugins/landmarks.cpp
@@ -125,7 +125,7 @@ class ProcrustesTransform : public Transform @@ -125,7 +125,7 @@ class ProcrustesTransform : public Transform
125 } 125 }
126 } 126 }
127 127
128 - void store(QDataStream &stream) const 128 + void store(QDataStream &stream, bool force) const
129 { 129 {
130 stream << meanShape; 130 stream << meanShape;
131 } 131 }
@@ -299,7 +299,7 @@ class ProcrustesAlignTransform : public Transform @@ -299,7 +299,7 @@ class ProcrustesAlignTransform : public Transform
299 dst.file.set("ProcrustesBound", QRectF(0, 0, width + 2 * padding, (qRound(width / aspectRatio) + 2 * padding))); 299 dst.file.set("ProcrustesBound", QRectF(0, 0, width + 2 * padding, (qRound(width / aspectRatio) + 2 * padding)));
300 } 300 }
301 301
302 - void store(QDataStream &stream) const 302 + void store(QDataStream &stream, bool force) const
303 { 303 {
304 stream << referenceShape; 304 stream << referenceShape;
305 stream << minX; 305 stream << minX;
openbr/plugins/meta.cpp
@@ -482,9 +482,9 @@ BR_REGISTER(Transform, CacheTransform) @@ -482,9 +482,9 @@ BR_REGISTER(Transform, CacheTransform)
482 class LoadStoreTransform : public MetaTransform 482 class LoadStoreTransform : public MetaTransform
483 { 483 {
484 Q_OBJECT 484 Q_OBJECT
485 - Q_PROPERTY(QString description READ get_description WRITE set_description RESET reset_description STORED false) 485 + Q_PROPERTY(QString description2 READ get_description2 WRITE set_description2 RESET reset_description2 STORED false)
486 Q_PROPERTY(QString fileName READ get_fileName WRITE set_fileName RESET reset_fileName STORED false) 486 Q_PROPERTY(QString fileName READ get_fileName WRITE set_fileName RESET reset_fileName STORED false)
487 - BR_PROPERTY(QString, description, "Identity") 487 + BR_PROPERTY(QString, description2, "Identity")
488 BR_PROPERTY(QString, fileName, QString()) 488 BR_PROPERTY(QString, fileName, QString())
489 489
490 Transform *transform; 490 Transform *transform;
@@ -493,6 +493,13 @@ class LoadStoreTransform : public MetaTransform @@ -493,6 +493,13 @@ class LoadStoreTransform : public MetaTransform
493 public: 493 public:
494 LoadStoreTransform() : transform(NULL) {} 494 LoadStoreTransform() : transform(NULL) {}
495 495
  496 + QString description(bool expanded = false)
  497 + {
  498 + if (expanded)
  499 + return transform->description(expanded);
  500 + return br::Object::description(expanded);
  501 + }
  502 +
496 bool setPropertyRecursive(const QString &name, QVariant value) 503 bool setPropertyRecursive(const QString &name, QVariant value)
497 { 504 {
498 if (br::Object::setPropertyRecursive(name, value)) 505 if (br::Object::setPropertyRecursive(name, value))
@@ -500,12 +507,22 @@ public: @@ -500,12 +507,22 @@ public:
500 return transform->setPropertyRecursive(name, value); 507 return transform->setPropertyRecursive(name, value);
501 } 508 }
502 private: 509 private:
  510 +
  511 + virtual void store(QDataStream &stream, bool force = false) const
  512 + {
  513 + if (force) {
  514 + transform->store(stream, force);
  515 + }
  516 +
  517 + br::Object::store(stream, false);
  518 + }
  519 +
503 void init() 520 void init()
504 { 521 {
505 if (transform != NULL) return; 522 if (transform != NULL) return;
506 - if (fileName.isEmpty()) baseName = QRegExp("^[a-zA-Z0-9]+$").exactMatch(description) ? description : QtUtils::shortTextHash(description); 523 + if (fileName.isEmpty()) baseName = QRegExp("^[a-zA-Z0-9]+$").exactMatch(description2) ? description2 : QtUtils::shortTextHash(description2);
507 else baseName = fileName; 524 else baseName = fileName;
508 - if (!tryLoad()) transform = make(description); 525 + if (!tryLoad()) transform = make(description2);
509 else trainable = false; 526 else trainable = false;
510 } 527 }
511 528
@@ -524,7 +541,7 @@ private: @@ -524,7 +541,7 @@ private:
524 qDebug("Storing %s", qPrintable(baseName)); 541 qDebug("Storing %s", qPrintable(baseName));
525 QByteArray byteArray; 542 QByteArray byteArray;
526 QDataStream stream(&byteArray, QFile::WriteOnly); 543 QDataStream stream(&byteArray, QFile::WriteOnly);
527 - stream << description; 544 + stream << description2;
528 transform->store(stream); 545 transform->store(stream);
529 QtUtils::writeFile(baseName, byteArray, -1); 546 QtUtils::writeFile(baseName, byteArray, -1);
530 } 547 }
@@ -570,8 +587,8 @@ private: @@ -570,8 +587,8 @@ private:
570 QByteArray data; 587 QByteArray data;
571 QtUtils::readFile(file, data, true); 588 QtUtils::readFile(file, data, true);
572 QDataStream stream(&data, QFile::ReadOnly); 589 QDataStream stream(&data, QFile::ReadOnly);
573 - stream >> description;  
574 - transform = Transform::make(description); 590 + stream >> description2;
  591 + transform = Transform::make(description2);
575 transform->load(stream); 592 transform->load(stream);
576 return true; 593 return true;
577 } 594 }
openbr/plugins/misc.cpp
@@ -617,8 +617,8 @@ class ProgressCounterTransform : public TimeVaryingTransform @@ -617,8 +617,8 @@ class ProgressCounterTransform : public TimeVaryingTransform
617 { 617 {
618 Q_OBJECT 618 Q_OBJECT
619 619
620 - Q_PROPERTY(int totalTemplates READ get_totalTemplates WRITE set_totalTemplates RESET reset_totalTemplates STORED false)  
621 - BR_PROPERTY(int, totalTemplates, 1) 620 + Q_PROPERTY(int totalProgress READ get_totalProgress WRITE set_totalProgress RESET reset_totalProgress STORED false)
  621 + BR_PROPERTY(int, totalProgress, 1)
622 622
623 void projectUpdate(const TemplateList &src, TemplateList &dst) 623 void projectUpdate(const TemplateList &src, TemplateList &dst)
624 { 624 {
@@ -658,16 +658,20 @@ class ProgressCounterTransform : public TimeVaryingTransform @@ -658,16 +658,20 @@ class ProgressCounterTransform : public TimeVaryingTransform
658 (void) data; 658 (void) data;
659 float p = br_progress(); 659 float p = br_progress();
660 qDebug("\r%05.2f%% ELAPSED=%s REMAINING=%s COUNT=%g", p*100, QtUtils::toTime(Globals->startTime.elapsed()/1000.0f).toStdString().c_str(), QtUtils::toTime(0).toStdString().c_str(), Globals->currentStep); 660 qDebug("\r%05.2f%% ELAPSED=%s REMAINING=%s COUNT=%g", p*100, QtUtils::toTime(Globals->startTime.elapsed()/1000.0f).toStdString().c_str(), QtUtils::toTime(0).toStdString().c_str(), Globals->currentStep);
  661 + timer.start();
  662 + Globals->startTime.start();
661 Globals->currentStep = 0; 663 Globals->currentStep = 0;
662 Globals->currentProgress = 0; 664 Globals->currentProgress = 0;
663 - Globals->totalSteps = 0; 665 + Globals->totalSteps = totalProgress;
664 } 666 }
665 667
666 void init() 668 void init()
667 { 669 {
668 timer.start(); 670 timer.start();
  671 + Globals->startTime.start();
669 Globals->currentStep = 0; 672 Globals->currentStep = 0;
670 Globals->currentProgress = 0; 673 Globals->currentProgress = 0;
  674 + Globals->totalSteps = totalProgress;
671 } 675 }
672 676
673 public: 677 public:
openbr/plugins/normalize.cpp
@@ -167,7 +167,7 @@ private: @@ -167,7 +167,7 @@ private:
167 divide(dst, a, dst); 167 divide(dst, a, dst);
168 } 168 }
169 169
170 - void store(QDataStream &stream) const 170 + void store(QDataStream &stream, bool force) const
171 { 171 {
172 stream << a << b; 172 stream << a << b;
173 } 173 }
@@ -256,7 +256,7 @@ class RowWiseMeanCenterTransform : public Transform @@ -256,7 +256,7 @@ class RowWiseMeanCenterTransform : public Transform
256 dst = m; 256 dst = m;
257 } 257 }
258 258
259 - void store(QDataStream &stream) const 259 + void store(QDataStream &stream, bool force) const
260 { 260 {
261 stream << mean; 261 stream << mean;
262 } 262 }
openbr/plugins/openbr_internal.h
@@ -19,7 +19,7 @@ protected: @@ -19,7 +19,7 @@ protected:
19 private: 19 private:
20 Transform *clone() const { return const_cast<UntrainableTransform*>(this); } 20 Transform *clone() const { return const_cast<UntrainableTransform*>(this); }
21 void train(const TemplateList &data) { (void) data; } 21 void train(const TemplateList &data) { (void) data; }
22 - void store(QDataStream &stream) const { (void) stream; } 22 + void store(QDataStream &stream, bool force) const { (void) stream; (void) force; }
23 void load(QDataStream &stream) { (void) stream; } 23 void load(QDataStream &stream) { (void) stream; }
24 }; 24 };
25 25
@@ -212,6 +212,45 @@ public: @@ -212,6 +212,45 @@ public:
212 } 212 }
213 return false; 213 return false;
214 } 214 }
  215 +
  216 + Transform * smartCopy(bool & newTransform)
  217 + {
  218 + if (!timeVarying()) {
  219 + newTransform = false;
  220 + return this;
  221 + }
  222 + newTransform = true;
  223 +
  224 + QString name = metaObject()->className();
  225 + name.replace("Transform","");
  226 + name += "(Identity";
  227 +
  228 + QStringList arguments = this->arguments();
  229 + if (!arguments.isEmpty()) {
  230 + name += ",";
  231 + name += this->arguments().join(",");
  232 + }
  233 +
  234 + name += ")";
  235 + name.replace("br::","");
  236 +
  237 + WrapperTransform *output = dynamic_cast<WrapperTransform *>(Transform::make(name, NULL));
  238 +
  239 + if (output == NULL)
  240 + qFatal("Dynamic cast failed!");
  241 +
  242 + bool newItem = false;
  243 + Transform * maybe_copy = transform->smartCopy(newItem);
  244 + if (newItem)
  245 + maybe_copy->setParent(output);
  246 + output->transform = maybe_copy;
  247 +
  248 + output->file = this->file;
  249 + output->init();
  250 +
  251 + return output;
  252 + }
  253 +
215 }; 254 };
216 255
217 /*! 256 /*!
@@ -388,6 +427,10 @@ public: @@ -388,6 +427,10 @@ public:
388 427
389 void applyAdditionalProperties(const File &temp, Transform *target); 428 void applyAdditionalProperties(const File &temp, Transform *target);
390 429
  430 +Transform *wrapTransform(Transform *base, const QString &target);
  431 +
  432 +Transform *pipeTransforms(QList<Transform *> &transforms);
  433 +
391 } 434 }
392 435
393 #endif // OPENBR_INTERNAL_H 436 #endif // OPENBR_INTERNAL_H
openbr/plugins/pp5.cpp
@@ -450,6 +450,7 @@ class PP5GalleryTransform: public UntrainableMetaTransform @@ -450,6 +450,7 @@ class PP5GalleryTransform: public UntrainableMetaTransform
450 450
451 ppr_gallery_type target; 451 ppr_gallery_type target;
452 QList<int> targetIDs; 452 QList<int> targetIDs;
  453 + TemplateList gallery;
453 454
454 void project(const Template & src, Template & dst) const 455 void project(const Template & src, Template & dst) const
455 { 456 {
@@ -495,11 +496,33 @@ class PP5GalleryTransform: public UntrainableMetaTransform @@ -495,11 +496,33 @@ class PP5GalleryTransform: public UntrainableMetaTransform
495 496
496 void init() 497 void init()
497 { 498 {
498 - // set up the gallery  
499 - ppr_create_gallery(context, &target);  
500 - TemplateList templates = TemplateList::fromGallery(galleryName);  
501 - enroll(templates,&target, targetIDs); 499 + if (!galleryName.isEmpty() || !gallery.isEmpty()) {
  500 + // set up the gallery
  501 + ppr_create_gallery(context, &target);
  502 + if (gallery.isEmpty() )
  503 + gallery = TemplateList::fromGallery(galleryName);
  504 + enroll(gallery, &target, targetIDs);
  505 + }
  506 + }
  507 +
  508 + void train(const TemplateList & data)
  509 + {
  510 + gallery = data;
502 } 511 }
  512 +
  513 + void store(QDataStream &stream, bool force) const
  514 + {
  515 + br::Object::store(stream, force);
  516 + stream << gallery;
  517 + }
  518 +
  519 + void load(QDataStream &stream)
  520 + {
  521 + br::Object::load(stream);
  522 + stream >> gallery;
  523 + init();
  524 + }
  525 +
503 }; 526 };
504 527
505 BR_REGISTER(Transform, PP5GalleryTransform) 528 BR_REGISTER(Transform, PP5GalleryTransform)
openbr/plugins/process.cpp
@@ -305,9 +305,7 @@ public: @@ -305,9 +305,7 @@ public:
305 while (!inbound || inbound->state() != QLocalSocket::ConnectedState) { 305 while (!inbound || inbound->state() != QLocalSocket::ConnectedState) {
306 bool res = receivedWait.wait(&receivedLock,30*1000); 306 bool res = receivedWait.wait(&receivedLock,30*1000);
307 if (!res) 307 if (!res)
308 - {  
309 qDebug() << key << " " << QThread::currentThread() << " waiting timed out, server thread is " << server.thread() << " base thread " << basis; 308 qDebug() << key << " " << QThread::currentThread() << " waiting timed out, server thread is " << server.thread() << " base thread " << basis;
310 - }  
311 } 309 }
312 } 310 }
313 311
@@ -338,6 +336,16 @@ public: @@ -338,6 +336,16 @@ public:
338 return true; 336 return true;
339 } 337 }
340 338
  339 + Transform * readTForm()
  340 + {
  341 + emit pulseReadSerialized();
  342 +
  343 + QByteArray data = readArray;
  344 + QDataStream deserializer(data);
  345 + Transform * res = Transform::deserialize(deserializer);
  346 + return res;
  347 + }
  348 +
341 template<typename T> 349 template<typename T>
342 bool sendData(const T & output) 350 bool sendData(const T & output)
343 { 351 {
@@ -401,6 +409,8 @@ public: @@ -401,6 +409,8 @@ public:
401 comm->connectToRemote(baseName+"_master"); 409 comm->connectToRemote(baseName+"_master");
402 410
403 comm->waitForInbound(); 411 comm->waitForInbound();
  412 +
  413 + transform = comm->readTForm();
404 } 414 }
405 415
406 void workerLoop() 416 void workerLoop()
@@ -428,7 +438,7 @@ public: @@ -428,7 +438,7 @@ public:
428 void WorkerProcess::mainLoop() 438 void WorkerProcess::mainLoop()
429 { 439 {
430 processInterface = new EnrollmentWorker(); 440 processInterface = new EnrollmentWorker();
431 - processInterface->transform = Transform::make(this->transform,NULL); 441 + processInterface->transform = NULL;
432 processInterface->connections(baseName); 442 processInterface->connections(baseName);
433 processInterface->workerLoop(); 443 processInterface->workerLoop();
434 delete processInterface; 444 delete processInterface;
@@ -478,63 +488,98 @@ protected slots: @@ -478,63 +488,98 @@ protected slots:
478 } 488 }
479 }; 489 };
480 490
  491 +struct ProcessData
  492 +{
  493 + CommunicationManager comm;
  494 + ProcessInterface proc;
  495 + bool initialized;
  496 + ProcessData()
  497 + {
  498 + initialized = false;
  499 + }
  500 +
  501 + ~ProcessData()
  502 + {
  503 + comm.sendSignal(CommunicationManager::SHOULD_END);
  504 + proc.endProcess();
  505 + comm.shutdown();
  506 + comm.shutDownThread();
  507 + }
  508 +};
  509 +
  510 +
481 /*! 511 /*!
482 * \ingroup transforms 512 * \ingroup transforms
483 * \brief Interface to a separate process 513 * \brief Interface to a separate process
484 * \author Charles Otto \cite caotto 514 * \author Charles Otto \cite caotto
485 */ 515 */
486 -class ProcessWrapperTransform : public TimeVaryingTransform 516 +class ProcessWrapperTransform : public WrapperTransform
487 { 517 {
488 Q_OBJECT 518 Q_OBJECT
489 519
490 - Q_PROPERTY(QString transform READ get_transform WRITE set_transform RESET reset_transform)  
491 - BR_PROPERTY(QString, transform, "")  
492 -  
493 QString baseKey; 520 QString baseKey;
494 521
495 - ProcessInterface workerProcess;  
496 - CommunicationManager * comm; 522 + Resource<ProcessData> processes;
  523 +
  524 + Transform * smartCopy(bool & newTransform)
  525 + {
  526 + newTransform = false;
  527 + return this;
  528 + }
497 529
498 - void projectUpdate(const TemplateList &src, TemplateList &dst) 530 + void project(const TemplateList &src, TemplateList &dst) const
499 { 531 {
500 if (src.empty()) 532 if (src.empty())
501 return; 533 return;
  534 +
  535 + ProcessData *data = processes.acquire();
  536 + if (!data->initialized)
  537 + activateProcess(data);
502 538
503 - if (!processActive)  
504 - {  
505 - activateProcess();  
506 - }  
507 - comm->sendSignal(CommunicationManager::INPUT_AVAILABLE);  
508 -  
509 - comm->sendData(src); 539 + CommunicationManager * localComm = &(data->comm);
  540 + localComm->sendSignal(CommunicationManager::INPUT_AVAILABLE);
510 541
511 - comm->readData(dst); 542 + localComm->sendData(src);
  543 + localComm->readData(dst);
  544 + processes.release(data);
512 } 545 }
513 546
514 -  
515 void train(const TemplateList& data) 547 void train(const TemplateList& data)
516 { 548 {
517 (void) data; 549 (void) data;
518 } 550 }
519 551
520 - // create the process  
521 void init() 552 void init()
522 { 553 {
523 processActive = false; 554 processActive = false;
  555 + serialized.clear();
  556 + if (transform) {
  557 + QDataStream out(&serialized, QFile::WriteOnly);
  558 + transform->serialize(out, true);
  559 + }
524 } 560 }
525 561
526 - void activateProcess() 562 + QByteArray serialized;
  563 + void transmitTForm(CommunicationManager * localComm) const
527 { 564 {
528 - comm = new CommunicationManager();  
529 - processActive = true; 565 + if (serialized.isEmpty() )
  566 + qFatal("Trying to transmit empty transform!");
  567 +
  568 + localComm->writeArray = serialized;
  569 + emit localComm->pulseSendSerialized();
  570 + }
530 571
  572 + void activateProcess(ProcessData * data) const
  573 + {
  574 + data->initialized = true;
531 // generate a uuid for our local servers 575 // generate a uuid for our local servers
532 QUuid id = QUuid::createUuid(); 576 QUuid id = QUuid::createUuid();
533 - baseKey = id.toString(); 577 + QString baseKey = id.toString();
534 578
535 QStringList argumentList; 579 QStringList argumentList;
  580 + // We serialize and transmit the transform directly, so algorithm doesn't matter.
536 argumentList.append("-algorithm"); 581 argumentList.append("-algorithm");
537 - argumentList.append(transform); 582 + argumentList.append("Identity");
538 if (!Globals->path.isEmpty()) { 583 if (!Globals->path.isEmpty()) {
539 argumentList.append("-path"); 584 argumentList.append("-path");
540 argumentList.append(Globals->path); 585 argumentList.append(Globals->path);
@@ -544,36 +589,25 @@ class ProcessWrapperTransform : public TimeVaryingTransform @@ -544,36 +589,25 @@ class ProcessWrapperTransform : public TimeVaryingTransform
544 argumentList.append("-slave"); 589 argumentList.append("-slave");
545 argumentList.append(baseKey); 590 argumentList.append(baseKey);
546 591
547 - comm->key = "master_"+baseKey.mid(1,5); 592 + data->comm.key = "master_"+baseKey.mid(1,5);
548 593
549 - comm->startServer(baseKey+"_master");  
550 - workerProcess.startProcess(argumentList);  
551 - comm->waitForInbound();  
552 - comm->connectToRemote(baseKey+"_worker");  
553 - } 594 + data->comm.startServer(baseKey+"_master");
554 595
555 - bool timeVarying() const {  
556 - return false; 596 + data->proc.startProcess(argumentList);
  597 + data->comm.waitForInbound();
  598 + data->comm.connectToRemote(baseKey+"_worker");
  599 +
  600 + transmitTForm(&(data->comm));
557 } 601 }
558 602
559 - ~ProcessWrapperTransform() 603 + bool timeVarying() const
560 { 604 {
561 - // end the process  
562 - if (this->processActive) {  
563 - comm->sendSignal(CommunicationManager::SHOULD_END);  
564 -  
565 - workerProcess.endProcess();  
566 - processActive = false;  
567 - comm->shutdown();  
568 - comm->shutDownThread();  
569 -  
570 - delete comm;  
571 - } 605 + return false;
572 } 606 }
573 607
574 public: 608 public:
575 bool processActive; 609 bool processActive;
576 - ProcessWrapperTransform() : TimeVaryingTransform(false,false) { processActive = false; } 610 + ProcessWrapperTransform() : WrapperTransform(false) { processActive = false; }
577 }; 611 };
578 612
579 BR_REGISTER(Transform, ProcessWrapperTransform) 613 BR_REGISTER(Transform, ProcessWrapperTransform)
openbr/plugins/quality.cpp
@@ -62,9 +62,9 @@ class ImpostorUniquenessMeasureTransform : public Transform @@ -62,9 +62,9 @@ class ImpostorUniquenessMeasureTransform : public Transform
62 dst.file.set("Impostor_Uniqueness_Measure_Bin", ium < mean-stddev ? 0 : (ium < mean+stddev ? 1 : 2)); 62 dst.file.set("Impostor_Uniqueness_Measure_Bin", ium < mean-stddev ? 0 : (ium < mean+stddev ? 1 : 2));
63 } 63 }
64 64
65 - void store(QDataStream &stream) const 65 + void store(QDataStream &stream, bool force) const
66 { 66 {
67 - distance->store(stream); 67 + distance->store(stream, force);
68 stream << mean << stddev << impostors; 68 stream << mean << stddev << impostors;
69 } 69 }
70 70
@@ -199,9 +199,9 @@ class MatchProbabilityDistance : public Distance @@ -199,9 +199,9 @@ class MatchProbabilityDistance : public Distance
199 return mp(score, gaussian); 199 return mp(score, gaussian);
200 } 200 }
201 201
202 - void store(QDataStream &stream) const 202 + void store(QDataStream &stream, bool force) const
203 { 203 {
204 - distance->store(stream); 204 + distance->store(stream, force);
205 stream << mp; 205 stream << mp;
206 } 206 }
207 207
@@ -264,9 +264,9 @@ class ZScoreDistance : public Distance @@ -264,9 +264,9 @@ class ZScoreDistance : public Distance
264 return score; 264 return score;
265 } 265 }
266 266
267 - void store(QDataStream &stream) const 267 + void store(QDataStream &stream, bool force) const
268 { 268 {
269 - distance->store(stream); 269 + distance->store(stream, force);
270 stream << min << max << mean << stddev; 270 stream << min << max << mean << stddev;
271 } 271 }
272 272
@@ -335,11 +335,11 @@ class HeatMapDistance : public Distance @@ -335,11 +335,11 @@ class HeatMapDistance : public Distance
335 } 335 }
336 } 336 }
337 337
338 - void store(QDataStream &stream) const 338 + void store(QDataStream &stream, bool force) const
339 { 339 {
340 stream << distances.size(); 340 stream << distances.size();
341 foreach (Distance *distance, distances) 341 foreach (Distance *distance, distances)
342 - distance->store(stream); 342 + distance->store(stream, force);
343 } 343 }
344 344
345 void load(QDataStream &stream) 345 void load(QDataStream &stream)
openbr/plugins/quantize.cpp
@@ -99,7 +99,7 @@ class HistEqQuantizationTransform : public Transform @@ -99,7 +99,7 @@ class HistEqQuantizationTransform : public Transform
99 } 99 }
100 } 100 }
101 101
102 - void store(QDataStream &stream) const 102 + void store(QDataStream &stream, bool force) const
103 { 103 {
104 stream << thresholds; 104 stream << thresholds;
105 } 105 }
@@ -174,7 +174,7 @@ class BayesianQuantizationDistance : public Distance @@ -174,7 +174,7 @@ class BayesianQuantizationDistance : public Distance
174 return likelihood; 174 return likelihood;
175 } 175 }
176 176
177 - void store(QDataStream &stream) const 177 + void store(QDataStream &stream, bool force) const
178 { 178 {
179 stream << loglikelihoods; 179 stream << loglikelihoods;
180 } 180 }
@@ -527,7 +527,7 @@ private: @@ -527,7 +527,7 @@ private:
527 dst.m().at<uchar>(0,sizeof(quint16)+i) = getIndex(m.colRange(max(0, i*step-offset), (i+1)*step-offset), centers[i]); 527 dst.m().at<uchar>(0,sizeof(quint16)+i) = getIndex(m.colRange(max(0, i*step-offset), (i+1)*step-offset), centers[i]);
528 } 528 }
529 529
530 - void store(QDataStream &stream) const 530 + void store(QDataStream &stream, bool force) const
531 { 531 {
532 stream << index << centers << ProductQuantizationLUTs[index]; 532 stream << index << centers << ProductQuantizationLUTs[index];
533 } 533 }
openbr/plugins/quantize2.cpp
@@ -104,7 +104,7 @@ class BayesianQuantizationTransform : public Transform @@ -104,7 +104,7 @@ class BayesianQuantizationTransform : public Transform
104 } 104 }
105 } 105 }
106 106
107 - void store(QDataStream &stream) const 107 + void store(QDataStream &stream, bool force) const
108 { 108 {
109 stream << thresholds; 109 stream << thresholds;
110 } 110 }
openbr/plugins/random.cpp
@@ -78,7 +78,7 @@ class RndSubspaceTransform : public Transform @@ -78,7 +78,7 @@ class RndSubspaceTransform : public Transform
78 remap(src, dst, map, Mat(), INTER_NEAREST); 78 remap(src, dst, map, Mat(), INTER_NEAREST);
79 } 79 }
80 80
81 - void store(QDataStream &stream) const 81 + void store(QDataStream &stream, bool force) const
82 { 82 {
83 stream << fraction << weighted << map; 83 stream << fraction << weighted << map;
84 } 84 }
openbr/plugins/slidingwindow.cpp
@@ -79,9 +79,9 @@ private: @@ -79,9 +79,9 @@ private:
79 } 79 }
80 } 80 }
81 81
82 - void store(QDataStream &stream) const 82 + void store(QDataStream &stream, bool force) const
83 { 83 {
84 - transform->store(stream); 84 + transform->store(stream, force);
85 stream << windowHeight; 85 stream << windowHeight;
86 } 86 }
87 87
@@ -289,9 +289,9 @@ private: @@ -289,9 +289,9 @@ private:
289 } 289 }
290 } 290 }
291 291
292 - void store(QDataStream &stream) const 292 + void store(QDataStream &stream, bool force) const
293 { 293 {
294 - transform->store(stream); 294 + transform->store(stream, force);
295 stream << aspectRatio << windowHeight; 295 stream << aspectRatio << windowHeight;
296 } 296 }
297 void load(QDataStream &stream) 297 void load(QDataStream &stream)
openbr/plugins/svm.cpp
@@ -172,7 +172,7 @@ private: @@ -172,7 +172,7 @@ private:
172 dst.file.set(outputVariable, reverseLookup[prediction]); 172 dst.file.set(outputVariable, reverseLookup[prediction]);
173 } 173 }
174 174
175 - void store(QDataStream &stream) const 175 + void store(QDataStream &stream, bool force) const
176 { 176 {
177 storeSVM(svm, stream); 177 storeSVM(svm, stream);
178 stream << labelMap << reverseLookup; 178 stream << labelMap << reverseLookup;
@@ -270,7 +270,7 @@ private: @@ -270,7 +270,7 @@ private:
270 return svm.predict(delta.reshape(1, 1)); 270 return svm.predict(delta.reshape(1, 1));
271 } 271 }
272 272
273 - void store(QDataStream &stream) const 273 + void store(QDataStream &stream, bool force) const
274 { 274 {
275 storeSVM(svm, stream); 275 storeSVM(svm, stream);
276 } 276 }
openbr/plugins/validate.cpp
@@ -114,11 +114,11 @@ class CrossValidateTransform : public MetaTransform @@ -114,11 +114,11 @@ class CrossValidateTransform : public MetaTransform
114 transforms[partition]->project(src, dst); 114 transforms[partition]->project(src, dst);
115 } 115 }
116 116
117 - void store(QDataStream &stream) const 117 + void store(QDataStream &stream, bool force) const
118 { 118 {
119 stream << transforms.size(); 119 stream << transforms.size();
120 foreach (Transform *transform, transforms) 120 foreach (Transform *transform, transforms)
121 - transform->store(stream); 121 + transform->store(stream, force);
122 } 122 }
123 123
124 void load(QDataStream &stream) 124 void load(QDataStream &stream)