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 26 struct AlgorithmCore
27 27 {
28 28 QSharedPointer<Transform> transform;
  29 + QSharedPointer<Transform> comparison;
29 30 QSharedPointer<Distance> distance;
30   - QString galleryCompareString;
31   -
32   - QString transformString;
33   - QString distanceString;
  31 + QSharedPointer<Transform> progressCounter;
34 32  
35 33 AlgorithmCore(const QString &name)
36 34 {
37 35 this->name = name;
38 36 init(name);
  37 + progressCounter = QSharedPointer<Transform>(Transform::make("ProgressCounter", NULL));
39 38 }
40 39  
41 40 bool isClassifier() const
42 41 {
43   - return distance.isNull();
  42 + return comparison.isNull();
44 43 }
45 44  
46 45 void train(const File &input, const QString &model)
... ... @@ -116,7 +115,9 @@ struct AlgorithmCore
116 115 in >> name; init(Globals->abbreviations.contains(name) ? Globals->abbreviations[name] : name);
117 116 transform->load(in);
118 117 bool hasDistance; in >> hasDistance;
119   - if (hasDistance) distance->load(in);
  118 +
  119 + if (hasDistance)
  120 + distance->load(in);
120 121 }
121 122  
122 123 File getMemoryGallery(const File &file) const
... ... @@ -148,54 +149,37 @@ struct AlgorithmCore
148 149 Gallery *temp = Gallery::make(input);
149 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 173 TemplateList data, output;
194 174 data.append(input);
195   - wrapper->projectUpdate(data, output);
  175 + progressCounter->setPropertyRecursive("totalProgress", QString::number(total));
  176 + stream->projectUpdate(data, output);
196 177  
197 178 files.append(output.files());
198 179  
  180 + if (multiProcess)
  181 + delete enroll;
  182 +
199 183 return files;
200 184 }
201 185  
... ... @@ -389,12 +373,12 @@ struct AlgorithmCore
389 373 // simple make sure the enrolled data is stored in a memGallery, but in multi-process mode we save the enrolled
390 374 // data to disk (as a .gal file) so that each worker process can read it without re-doing enrollment.
391 375 File colEnrolledGallery = colGallery;
392   - QString targetExtension = multiProcess ? "gal" : "mem";
  376 + QString targetExtension = "mem";
393 377  
394 378 // If the column gallery is not already of the appropriate type, we need to do something
395 379 if (colGallery.suffix() != targetExtension) {
396 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 383 // Check if we have to do real enrollment, and not just convert the gallery's type.
400 384 if (!(QStringList() << "gal" << "template" << "mem").contains(colGallery.suffix()))
... ... @@ -435,37 +419,23 @@ struct AlgorithmCore
435 419 // The actual comparison step is done by a GalleryCompare transform, which has a Distance, and a gallery as data.
436 420 // Incoming templates are compared against the templates in the gallery, and the output is the resulting score
437 421 // vector.
  422 + TemplateList tlist = TemplateList::fromGallery(colEnrolledGallery);
  423 + comparison->train(tlist);
  424 + comparison->setPropertyRecursive("galleryName","");
  425 +
438 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 440 // At this point, compareRegion is a transform, which optionally does enrollment, then compares the row
471 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 443  
474 444 // We also need to add Output and progress counting to the algorithm we are building, so we will assign them to
475 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 449 // The output transform takes the metadata memGalleries we set up previously as input, along with the
480 450 // output specification we were passed. Gallery metadata is necessary for some Outputs to function correctly.
481 451 QString outputString = output.flat().isEmpty() ? "Empty" : output.flat();
482 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 461 // With this, we have set up a transform which (optionally) enrolls templates, compares them
494 462 // against a gallery, and outputs them.
495   - join->init();
  463 + Transform * pipeline = br::pipeTransforms(compareOutput);
496 464  
497 465 // Now, we will give that base transform to a stream, which will incrementally read the row gallery
498 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 469 // We set up a template containing the rowGallery we want to compare.
508 470 TemplateList rowGalleryTemplate;
509 471 rowGalleryTemplate.append(Template(rowGallery));
510 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 477 // Do the actual comparisons
519 478 streamWrapper->projectUpdate(rowGalleryTemplate, outputGallery);
... ... @@ -557,27 +516,22 @@ private:
557 516 return;
558 517 }
559 518  
  519 + //! [Parsing the algorithm description]
560 520 const bool compareTransform = description.contains('!');
561 521 QStringList words = QtUtils::parse(description, compareTransform ? '!' : ':');
562 522  
563 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 525 //! [Creating the template generation and comparison methods]
569 526 transform = QSharedPointer<Transform>(Transform::make(words[0], NULL));
570 527 if (words.size() > 1) {
571 528 if (!compareTransform) {
572 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 536 //! [Creating the template generation and comparison methods]
583 537 }
... ...
openbr/openbr_plugin.cpp
... ... @@ -577,16 +577,31 @@ QStringList Object::parameters() const
577 577 return parameters;
578 578 }
579 579  
580   -QStringList Object::arguments() const
  580 +QStringList Object::arguments(bool expanded)
581 581 {
582 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 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 606 if ((index < 0) || (index > metaObject()->propertyCount())) return "";
592 607 const QMetaProperty property = metaObject()->property(index);
... ... @@ -604,19 +619,19 @@ QString Object::argument(int index) const
604 619 strings.append(QString::number(value));
605 620 } else if (type == "QList<br::Transform*>") {
606 621 foreach (Transform *transform, variant.value< QList<Transform*> >())
607   - strings.append(transform->description());
  622 + strings.append(transform->description(expanded));
608 623 } else if (type == "QList<br::Distance*>") {
609 624 foreach (Distance *distance, variant.value< QList<Distance*> >())
610   - strings.append(distance->description());
  625 + strings.append(distance->description(expanded));
611 626 } else {
612 627 qFatal("Unrecognized type: %s", qPrintable(type));
613 628 }
614 629  
615 630 return "[" + strings.join(",") + "]";
616 631 } else if (type == "br::Transform*") {
617   - return variant.value<Transform*>()->description();
  632 + return variant.value<Transform*>()->description(expanded);
618 633 } else if (type == "br::Distance*") {
619   - return variant.value<Distance*>()->description();
  634 + return variant.value<Distance*>()->description(expanded);
620 635 } else if (type == "QStringList") {
621 636 return "[" + variant.toStringList().join(",") + "]";
622 637 }
... ... @@ -624,13 +639,17 @@ QString Object::argument(int index) const
624 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 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 654 // Start from 1 to skip QObject::objectName
636 655 for (int i=1; i<metaObject()->propertyCount(); i++) {
... ... @@ -641,14 +660,14 @@ void Object::store(QDataStream &amp;stream) const
641 660 const QString type = property.typeName();
642 661 if (type == "QList<br::Transform*>") {
643 662 foreach (Transform *transform, property.read(this).value< QList<Transform*> >())
644   - transform->store(stream);
  663 + transform->store(stream, force);
645 664 } else if (type == "QList<br::Distance*>") {
646 665 foreach (Distance *distance, property.read(this).value< QList<Distance*> >())
647   - distance->store(stream);
  666 + distance->store(stream, force);
648 667 } else if (type == "br::Transform*") {
649   - property.read(this).value<Transform*>()->store(stream);
  668 + property.read(this).value<Transform*>()->store(stream, force);
650 669 } else if (type == "br::Distance*") {
651   - property.read(this).value<Distance*>()->store(stream);
  670 + property.read(this).value<Distance*>()->store(stream, force);
652 671 } else if (type == "bool") {
653 672 stream << property.read(this).toBool();
654 673 } else if (type == "int") {
... ... @@ -1284,7 +1303,8 @@ Transform *Transform::make(QString str, QObject *parent)
1284 1303  
1285 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 1308 return clone;
1289 1309 }
1290 1310  
... ... @@ -1453,3 +1473,17 @@ void br::applyAdditionalProperties(const File &amp;temp, Transform *target)
1453 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 589 File file; /*!< \brief The file used to construct the plugin. */
590 590  
591 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 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 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 606 void setProperty(const QString &name, QVariant value); /*!< \brief Overload of QObject::setProperty to handle OpenBR data types. */
600 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 1280 */
1274 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 1292 protected:
1277 1293 Transform(bool independent = true, bool trainable = true); /*!< \brief Construct a transform. */
1278 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 415 }
416 416  
417 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 420 int size = 1;
421 421 stream << size;
... ...
openbr/plugins/cluster.cpp
... ... @@ -69,7 +69,7 @@ class KMeansTransform : public Transform
69 69 reindex();
70 70 }
71 71  
72   - void store(QDataStream &stream) const
  72 + void store(QDataStream &stream, bool force) const
73 73 {
74 74 stream << centers;
75 75 }
... ... @@ -131,7 +131,7 @@ class KNNTransform : public Transform
131 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 136 stream << gallery;
137 137 }
... ... @@ -196,7 +196,7 @@ class RandomCentroidsTransform : public Transform
196 196 reindex();
197 197 }
198 198  
199   - void store(QDataStream &stream) const
  199 + void store(QDataStream &stream, bool force) const
200 200 {
201 201 stream << centers;
202 202 }
... ...
openbr/plugins/distance.cpp
... ... @@ -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 258 stream << distances.size();
259 259 foreach (Distance *distance, distances)
260   - distance->store(stream);
  260 + distance->store(stream, force);
261 261 }
262 262  
263 263 void load(QDataStream &stream)
... ... @@ -328,9 +328,9 @@ class NegativeLogPlusOneDistance : public Distance
328 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 336 void load(QDataStream &stream)
... ... @@ -460,13 +460,12 @@ BR_REGISTER(Distance, SumDistance)
460 460 class GalleryCompareTransform : public Transform
461 461 {
462 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 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 466 BR_PROPERTY(QString, galleryName, "")
467 467  
468 468 TemplateList gallery;
469   - QSharedPointer<Distance> distance;
470 469  
471 470 void project(const Template &src, Template &dst) const
472 471 {
... ... @@ -480,16 +479,29 @@ class GalleryCompareTransform : public Transform
480 479  
481 480 void init()
482 481 {
483   - if (!galleryName.isEmpty()) {
  482 + if (!galleryName.isEmpty())
484 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 503 public:
492   - GalleryCompareTransform() : Transform(false, false) {}
  504 + GalleryCompareTransform() : Transform(false, true) {}
493 505 };
494 506  
495 507 BR_REGISTER(Transform, GalleryCompareTransform)
... ...
openbr/plugins/eigen3.cpp
... ... @@ -119,7 +119,7 @@ private:
119 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 124 stream << keep << drop << whiten << originalRows << mean << eVals << eVecs;
125 125 }
... ... @@ -295,9 +295,9 @@ class DFFSTransform : public Transform
295 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 303 void load(QDataStream &stream)
... ... @@ -525,7 +525,7 @@ class LDATransform : public Transform
525 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 530 stream << pcaKeep;
531 531 stream << directLDA;
... ... @@ -631,10 +631,10 @@ class SparseLDATransform : public Transform
631 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 636 stream << pcaKeep;
637   - stream << ldaSparse;
  637 + ldaSparse.store(stream, force);
638 638 stream << dimsOut;
639 639 stream << selections;
640 640 }
... ...
openbr/plugins/frames.cpp
... ... @@ -43,7 +43,7 @@ private:
43 43 buffer.clear();
44 44 }
45 45  
46   - void store(QDataStream &stream) const
  46 + void store(QDataStream &stream, bool force) const
47 47 {
48 48 (void) stream;
49 49 }
... ...
openbr/plugins/independent.cpp
... ... @@ -126,6 +126,10 @@ class IndependentTransform : public MetaTransform
126 126  
127 127 QList<Transform*> transforms;
128 128  
  129 + QString description(bool expanded)
  130 + {
  131 + return transform->description(expanded);
  132 + }
129 133  
130 134 bool setPropertyRecursive(const QString &name, QVariant value)
131 135 {
... ... @@ -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 235 const int size = transforms.size();
232 236 stream << size;
233 237 for (int i=0; i<size; i++)
234   - transforms[i]->store(stream);
  238 + transforms[i]->store(stream, force);
235 239 }
236 240  
237 241 void load(QDataStream &stream)
... ... @@ -292,10 +296,10 @@ class SingletonTransform : public MetaTransform
292 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 301 if (transform->parent() == this)
298   - transform->store(stream);
  302 + transform->store(stream, force);
299 303 }
300 304  
301 305 void load(QDataStream &stream)
... ...
openbr/plugins/integral.cpp
... ... @@ -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 257 stream << (subTransform != NULL);
258 258 if (subTransform != NULL)
259   - subTransform->store(stream);
  259 + subTransform->store(stream, force);
260 260 }
261 261  
262 262 void load(QDataStream &stream)
... ...
openbr/plugins/landmarks.cpp
... ... @@ -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 130 stream << meanShape;
131 131 }
... ... @@ -299,7 +299,7 @@ class ProcrustesAlignTransform : public Transform
299 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 304 stream << referenceShape;
305 305 stream << minX;
... ...
openbr/plugins/meta.cpp
... ... @@ -482,9 +482,9 @@ BR_REGISTER(Transform, CacheTransform)
482 482 class LoadStoreTransform : public MetaTransform
483 483 {
484 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 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 488 BR_PROPERTY(QString, fileName, QString())
489 489  
490 490 Transform *transform;
... ... @@ -493,6 +493,13 @@ class LoadStoreTransform : public MetaTransform
493 493 public:
494 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 503 bool setPropertyRecursive(const QString &name, QVariant value)
497 504 {
498 505 if (br::Object::setPropertyRecursive(name, value))
... ... @@ -500,12 +507,22 @@ public:
500 507 return transform->setPropertyRecursive(name, value);
501 508 }
502 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 520 void init()
504 521 {
505 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 524 else baseName = fileName;
508   - if (!tryLoad()) transform = make(description);
  525 + if (!tryLoad()) transform = make(description2);
509 526 else trainable = false;
510 527 }
511 528  
... ... @@ -524,7 +541,7 @@ private:
524 541 qDebug("Storing %s", qPrintable(baseName));
525 542 QByteArray byteArray;
526 543 QDataStream stream(&byteArray, QFile::WriteOnly);
527   - stream << description;
  544 + stream << description2;
528 545 transform->store(stream);
529 546 QtUtils::writeFile(baseName, byteArray, -1);
530 547 }
... ... @@ -570,8 +587,8 @@ private:
570 587 QByteArray data;
571 588 QtUtils::readFile(file, data, true);
572 589 QDataStream stream(&data, QFile::ReadOnly);
573   - stream >> description;
574   - transform = Transform::make(description);
  590 + stream >> description2;
  591 + transform = Transform::make(description2);
575 592 transform->load(stream);
576 593 return true;
577 594 }
... ...
openbr/plugins/misc.cpp
... ... @@ -617,8 +617,8 @@ class ProgressCounterTransform : public TimeVaryingTransform
617 617 {
618 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 623 void projectUpdate(const TemplateList &src, TemplateList &dst)
624 624 {
... ... @@ -658,16 +658,20 @@ class ProgressCounterTransform : public TimeVaryingTransform
658 658 (void) data;
659 659 float p = br_progress();
660 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 663 Globals->currentStep = 0;
662 664 Globals->currentProgress = 0;
663   - Globals->totalSteps = 0;
  665 + Globals->totalSteps = totalProgress;
664 666 }
665 667  
666 668 void init()
667 669 {
668 670 timer.start();
  671 + Globals->startTime.start();
669 672 Globals->currentStep = 0;
670 673 Globals->currentProgress = 0;
  674 + Globals->totalSteps = totalProgress;
671 675 }
672 676  
673 677 public:
... ...
openbr/plugins/normalize.cpp
... ... @@ -167,7 +167,7 @@ private:
167 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 172 stream << a << b;
173 173 }
... ... @@ -256,7 +256,7 @@ class RowWiseMeanCenterTransform : public Transform
256 256 dst = m;
257 257 }
258 258  
259   - void store(QDataStream &stream) const
  259 + void store(QDataStream &stream, bool force) const
260 260 {
261 261 stream << mean;
262 262 }
... ...
openbr/plugins/openbr_internal.h
... ... @@ -19,7 +19,7 @@ protected:
19 19 private:
20 20 Transform *clone() const { return const_cast<UntrainableTransform*>(this); }
21 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 23 void load(QDataStream &stream) { (void) stream; }
24 24 };
25 25  
... ... @@ -212,6 +212,45 @@ public:
212 212 }
213 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 427  
389 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 436 #endif // OPENBR_INTERNAL_H
... ...
openbr/plugins/pp5.cpp
... ... @@ -450,6 +450,7 @@ class PP5GalleryTransform: public UntrainableMetaTransform
450 450  
451 451 ppr_gallery_type target;
452 452 QList<int> targetIDs;
  453 + TemplateList gallery;
453 454  
454 455 void project(const Template & src, Template & dst) const
455 456 {
... ... @@ -495,11 +496,33 @@ class PP5GalleryTransform: public UntrainableMetaTransform
495 496  
496 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 528 BR_REGISTER(Transform, PP5GalleryTransform)
... ...
openbr/plugins/process.cpp
... ... @@ -305,9 +305,7 @@ public:
305 305 while (!inbound || inbound->state() != QLocalSocket::ConnectedState) {
306 306 bool res = receivedWait.wait(&receivedLock,30*1000);
307 307 if (!res)
308   - {
309 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 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 349 template<typename T>
342 350 bool sendData(const T & output)
343 351 {
... ... @@ -401,6 +409,8 @@ public:
401 409 comm->connectToRemote(baseName+"_master");
402 410  
403 411 comm->waitForInbound();
  412 +
  413 + transform = comm->readTForm();
404 414 }
405 415  
406 416 void workerLoop()
... ... @@ -428,7 +438,7 @@ public:
428 438 void WorkerProcess::mainLoop()
429 439 {
430 440 processInterface = new EnrollmentWorker();
431   - processInterface->transform = Transform::make(this->transform,NULL);
  441 + processInterface->transform = NULL;
432 442 processInterface->connections(baseName);
433 443 processInterface->workerLoop();
434 444 delete processInterface;
... ... @@ -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 512 * \ingroup transforms
483 513 * \brief Interface to a separate process
484 514 * \author Charles Otto \cite caotto
485 515 */
486   -class ProcessWrapperTransform : public TimeVaryingTransform
  516 +class ProcessWrapperTransform : public WrapperTransform
487 517 {
488 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 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 532 if (src.empty())
501 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 547 void train(const TemplateList& data)
516 548 {
517 549 (void) data;
518 550 }
519 551  
520   - // create the process
521 552 void init()
522 553 {
523 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 575 // generate a uuid for our local servers
532 576 QUuid id = QUuid::createUuid();
533   - baseKey = id.toString();
  577 + QString baseKey = id.toString();
534 578  
535 579 QStringList argumentList;
  580 + // We serialize and transmit the transform directly, so algorithm doesn't matter.
536 581 argumentList.append("-algorithm");
537   - argumentList.append(transform);
  582 + argumentList.append("Identity");
538 583 if (!Globals->path.isEmpty()) {
539 584 argumentList.append("-path");
540 585 argumentList.append(Globals->path);
... ... @@ -544,36 +589,25 @@ class ProcessWrapperTransform : public TimeVaryingTransform
544 589 argumentList.append("-slave");
545 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 608 public:
575 609 bool processActive;
576   - ProcessWrapperTransform() : TimeVaryingTransform(false,false) { processActive = false; }
  610 + ProcessWrapperTransform() : WrapperTransform(false) { processActive = false; }
577 611 };
578 612  
579 613 BR_REGISTER(Transform, ProcessWrapperTransform)
... ...
openbr/plugins/quality.cpp
... ... @@ -62,9 +62,9 @@ class ImpostorUniquenessMeasureTransform : public Transform
62 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 68 stream << mean << stddev << impostors;
69 69 }
70 70  
... ... @@ -199,9 +199,9 @@ class MatchProbabilityDistance : public Distance
199 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 205 stream << mp;
206 206 }
207 207  
... ... @@ -264,9 +264,9 @@ class ZScoreDistance : public Distance
264 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 270 stream << min << max << mean << stddev;
271 271 }
272 272  
... ... @@ -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 340 stream << distances.size();
341 341 foreach (Distance *distance, distances)
342   - distance->store(stream);
  342 + distance->store(stream, force);
343 343 }
344 344  
345 345 void load(QDataStream &stream)
... ...
openbr/plugins/quantize.cpp
... ... @@ -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 104 stream << thresholds;
105 105 }
... ... @@ -174,7 +174,7 @@ class BayesianQuantizationDistance : public Distance
174 174 return likelihood;
175 175 }
176 176  
177   - void store(QDataStream &stream) const
  177 + void store(QDataStream &stream, bool force) const
178 178 {
179 179 stream << loglikelihoods;
180 180 }
... ... @@ -527,7 +527,7 @@ private:
527 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 532 stream << index << centers << ProductQuantizationLUTs[index];
533 533 }
... ...
openbr/plugins/quantize2.cpp
... ... @@ -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 109 stream << thresholds;
110 110 }
... ...
openbr/plugins/random.cpp
... ... @@ -78,7 +78,7 @@ class RndSubspaceTransform : public Transform
78 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 83 stream << fraction << weighted << map;
84 84 }
... ...
openbr/plugins/slidingwindow.cpp
... ... @@ -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 85 stream << windowHeight;
86 86 }
87 87  
... ... @@ -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 295 stream << aspectRatio << windowHeight;
296 296 }
297 297 void load(QDataStream &stream)
... ...
openbr/plugins/svm.cpp
... ... @@ -172,7 +172,7 @@ private:
172 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 177 storeSVM(svm, stream);
178 178 stream << labelMap << reverseLookup;
... ... @@ -270,7 +270,7 @@ private:
270 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 275 storeSVM(svm, stream);
276 276 }
... ...
openbr/plugins/validate.cpp
... ... @@ -114,11 +114,11 @@ class CrossValidateTransform : public MetaTransform
114 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 119 stream << transforms.size();
120 120 foreach (Transform *transform, transforms)
121   - transform->store(stream);
  121 + transform->store(stream, force);
122 122 }
123 123  
124 124 void load(QDataStream &stream)
... ...