Commit 36d2607d60d85383c75edfd11a401aa4b8baece7

Authored by caotto
2 parents 496bc87c 5c6a8fab

Merge pull request #216 from biometrics/serialization_update

Better support for serializing algorithms in memory
CMakeLists.txt
... ... @@ -121,7 +121,7 @@ else()
121 121 set(CMAKE_EXE_LINKER_FLAGS "-Wl,--enable-auto-import") # Fixes a linker warning
122 122 set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--enable-auto-import")
123 123 elseif(MSVC)
124   - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W3 /DNOMINMAX /D_CRT_SECURE_NO_WARNINGS /wd4018 /wd4244 /wd4267 /wd4305 /wd4308 /wd4307 /wd4554 /wd4996 /nologo /MP")
  124 + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W3 /DNOMINMAX /D_CRT_SECURE_NO_WARNINGS /wd4018 /wd4244 /wd4267 /wd4305 /wd4308 /wd4307 /wd4554 /wd4996 /w34100 /nologo /MP")
125 125 endif()
126 126 endif()
127 127  
... ...
openbr/core/core.cpp
... ... @@ -23,24 +23,36 @@
23 23  
24 24 namespace br {
25 25  
  26 +void noDelete(Transform *target)
  27 +{
  28 + (void) target;
  29 +}
  30 +
26 31 struct AlgorithmCore
27 32 {
  33 + enum CompareMode
  34 + {
  35 + None,
  36 + DistanceCompare,
  37 + TransformCompare,
  38 + };
  39 +
28 40 QSharedPointer<Transform> transform;
  41 + QSharedPointer<Transform> simplifiedTransform;
  42 + QSharedPointer<Transform> comparison;
29 43 QSharedPointer<Distance> distance;
30   - QString galleryCompareString;
31   -
32   - QString transformString;
33   - QString distanceString;
  44 + QSharedPointer<Transform> progressCounter;
34 45  
35 46 AlgorithmCore(const QString &name)
36 47 {
37 48 this->name = name;
38 49 init(name);
  50 + progressCounter = QSharedPointer<Transform>(Transform::make("ProgressCounter", NULL));
39 51 }
40 52  
41 53 bool isClassifier() const
42 54 {
43   - return distance.isNull();
  55 + return comparison.isNull();
44 56 }
45 57  
46 58 void train(const File &input, const QString &model)
... ... @@ -48,15 +60,7 @@ struct AlgorithmCore
48 60 qDebug("Training on %s%s", qPrintable(input.flat()),
49 61 model.isEmpty() ? "" : qPrintable(" to " + model));
50 62  
51   - QScopedPointer<Transform> trainingWrapper(Transform::make("DirectStream(readMode=DistributeFrames)", NULL));
52   -
53   - CompositeTransform *downcast = dynamic_cast<CompositeTransform *>(trainingWrapper.data());
54   - if (downcast == NULL)
55   - qFatal("downcast failed?");
56   - downcast->transforms.append(this->transform.data());
57   -
58   - downcast->init();
59   -
  63 + QScopedPointer<Transform> trainingWrapper(br::wrapTransform(transform.data(), "Stream(readMode=DistributeFrames)"));
60 64 TemplateList data(TemplateList::fromGallery(input));
61 65  
62 66 if (transform.isNull()) qFatal("Null transform.");
... ... @@ -65,14 +69,14 @@ struct AlgorithmCore
65 69 Globals->startTime.start();
66 70  
67 71 qDebug("Training Enrollment");
68   - downcast->train(data);
  72 + trainingWrapper->train(data);
69 73  
70 74 if (!distance.isNull()) {
71 75 if (Globals->crossValidate > 0)
72 76 for (int i=data.size()-1; i>=0; i--) if (data[i].file.get<bool>("allPartitions",false)) data.removeAt(i);
73 77  
74 78 qDebug("Projecting Enrollment");
75   - downcast->projectUpdate(data,data);
  79 + trainingWrapper->projectUpdate(data,data);
76 80  
77 81 qDebug("Training Comparison");
78 82 distance->train(data);
... ... @@ -84,6 +88,18 @@ struct AlgorithmCore
84 88 }
85 89  
86 90 qDebug("Training Time: %s", qPrintable(QtUtils::toTime(Globals->startTime.elapsed()/1000.0f)));
  91 +
  92 + simplifyTransform();
  93 + }
  94 +
  95 + void simplifyTransform()
  96 + {
  97 + bool newTForm = false;
  98 + Transform *temp = transform->simplify(newTForm);
  99 + if (newTForm)
  100 + simplifiedTransform = QSharedPointer<Transform>(temp);
  101 + else
  102 + simplifiedTransform = QSharedPointer<Transform>(temp, noDelete);
87 103 }
88 104  
89 105 void store(const QString &model) const
... ... @@ -93,11 +109,21 @@ struct AlgorithmCore
93 109 QDataStream out(&data, QFile::WriteOnly);
94 110  
95 111 // Serialize algorithm to stream
96   - out << name;
97   - transform->store(out);
98   - const bool hasComparer = !distance.isNull();
99   - out << hasComparer;
100   - if (hasComparer) distance->store(out);
  112 + transform->serialize(out);
  113 +
  114 + qint32 mode = None;
  115 + if (!distance.isNull())
  116 + mode = DistanceCompare;
  117 + else if (!comparison.isNull())
  118 + mode = TransformCompare;
  119 +
  120 + out << mode;
  121 +
  122 + if (mode == DistanceCompare)
  123 + distance->serialize(out);
  124 +
  125 + if (mode == TransformCompare)
  126 + comparison->serialize(out);
101 127  
102 128 // Compress and save to file
103 129 QtUtils::writeFile(model, data, -1);
... ... @@ -113,10 +139,21 @@ struct AlgorithmCore
113 139 QDataStream in(&data, QFile::ReadOnly);
114 140  
115 141 // Load algorithm
116   - in >> name; init(Globals->abbreviations.contains(name) ? Globals->abbreviations[name] : name);
117   - transform->load(in);
118   - bool hasDistance; in >> hasDistance;
119   - if (hasDistance) distance->load(in);
  142 + transform = QSharedPointer<Transform>(Transform::deserialize(in));
  143 +
  144 + qint32 mode;
  145 + in >> mode;
  146 +
  147 + if (mode == DistanceCompare) {
  148 + QString distanceDescription;
  149 + in >> distanceDescription;
  150 + distance = QSharedPointer<Distance>(Distance::make(distanceDescription, NULL));
  151 + distance->load(in);
  152 + comparison = QSharedPointer<Transform>(Transform::make("GalleryCompare", NULL));
  153 + comparison->setPropertyRecursive("distance", QVariant::fromValue(distance.data()));
  154 + }
  155 + if (mode == TransformCompare)
  156 + comparison = QSharedPointer<Transform>(Transform::deserialize(in));
120 157 }
121 158  
122 159 File getMemoryGallery(const File &file) const
... ... @@ -148,54 +185,38 @@ struct AlgorithmCore
148 185 Gallery *temp = Gallery::make(input);
149 186 qint64 total = temp->totalSize();
150 187  
151   - QScopedPointer<Transform> basePipe;
152   -
153   - QString pipeDesc = "GalleryOutput("+gallery.flat()+")+ProgressCounter("+QString::number(total)+")+Discard";
  188 + Transform *enroll = simplifiedTransform.data();
154 189  
155   - if (!multiProcess) {
156   - basePipe.reset(Transform::make(pipeDesc,NULL));
157   - CompositeTransform *downcast = dynamic_cast<CompositeTransform *>(basePipe.data());
  190 + if (multiProcess)
  191 + enroll = wrapTransform(enroll, "ProcessWrapper");
158 192  
159   - if (downcast == NULL) qFatal("downcast failed?");
160   -
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;
174   -
175   - basePipe.reset(Transform::make(pipeDesc,NULL));
176   - }
  193 + QList<Transform *> stages;
  194 + stages.append(enroll);
177 195  
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());
  196 + QString outputDesc;
  197 + if (fileExclusion)
  198 + outputDesc = "FileExclusion(" + gallery.flat() + ")+";
  199 + outputDesc.append("GalleryOutput("+gallery.flat()+")");
  200 + QScopedPointer<Transform> outputTform(Transform::make(outputDesc, NULL));
  201 + stages.append(outputTform.data());
  202 + stages.append(progressCounter.data());
  203 + QScopedPointer<Transform> discard(Transform::make("Discard",NULL));
  204 + stages.append(discard.data());
182 205  
183   - // replace that placeholder with the pipe we built
184   - wrapper->transform = basePipe.data();
  206 + QScopedPointer<Transform> pipeline(br::pipeTransforms(stages));
185 207  
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;
  208 + QScopedPointer<Transform> stream(br::wrapTransform(pipeline.data(), "Stream(readMode=StreamGallery)"));
192 209  
193 210 TemplateList data, output;
194 211 data.append(input);
195   - wrapper->projectUpdate(data, output);
  212 + progressCounter->setPropertyRecursive("totalProgress", QString::number(total));
  213 + stream->projectUpdate(data, output);
196 214  
197 215 files.append(output.files());
198 216  
  217 + if (multiProcess)
  218 + delete enroll;
  219 +
199 220 return files;
200 221 }
201 222  
... ... @@ -364,7 +385,6 @@ struct AlgorithmCore
364 385 targetMetadata = FileList::fromGallery(targetGallery, true);
365 386 queryMetadata = FileList::fromGallery(queryGallery, true);
366 387  
367   -
368 388 // Is the target or query set larger? We will use the larger as the rows of our comparison matrix (and transpose the output if necessary)
369 389 transposeMode = targetMetadata.size() > queryMetadata.size();
370 390  
... ... @@ -389,12 +409,12 @@ struct AlgorithmCore
389 409 // simple make sure the enrolled data is stored in a memGallery, but in multi-process mode we save the enrolled
390 410 // data to disk (as a .gal file) so that each worker process can read it without re-doing enrollment.
391 411 File colEnrolledGallery = colGallery;
392   - QString targetExtension = multiProcess ? "gal" : "mem";
  412 + QString targetExtension = "mem";
393 413  
394 414 // If the column gallery is not already of the appropriate type, we need to do something
395 415 if (colGallery.suffix() != targetExtension) {
396 416 // Build the name of a gallery containing the enrolled data, of the appropriate type.
397   - colEnrolledGallery = colGallery.baseName() + colGallery.hash() + (multiProcess ? ".gal" : ".mem");
  417 + colEnrolledGallery = colGallery.baseName() + colGallery.hash() + '.' + targetExtension;
398 418  
399 419 // Check if we have to do real enrollment, and not just convert the gallery's type.
400 420 if (!(QStringList() << "gal" << "template" << "mem").contains(colGallery.suffix()))
... ... @@ -435,37 +455,24 @@ struct AlgorithmCore
435 455 // The actual comparison step is done by a GalleryCompare transform, which has a Distance, and a gallery as data.
436 456 // Incoming templates are compared against the templates in the gallery, and the output is the resulting score
437 457 // vector.
  458 + TemplateList tlist = TemplateList::fromGallery(colEnrolledGallery);
  459 + comparison->train(tlist);
  460 + comparison->setPropertyRecursive("galleryName","");
  461 +
438 462 QString compareRegionDesc;
  463 + QList<Transform *> enrollCompare;
  464 + enrollCompare.append(comparison.data());
439 465  
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   - }
  466 + // if we have to enroll the row gallery, add that transform to the list
  467 + if (needEnrollRows)
  468 + enrollCompare.prepend(simplifiedTransform.data());
  469 +
  470 + Transform *compareRegionBase = pipeTransforms(enrollCompare);
  471 + // If in multi-process mode, wrap the enroll+compare structure in a ProcessWrapper.
  472 + if (multiProcess)
  473 + compareRegionBase = wrapTransform(compareRegionBase, "ProcessWrapper");
  474 +
  475 + QScopedPointer<Transform> compareRegion(compareRegionBase);
469 476  
470 477 // At this point, compareRegion is a transform, which optionally does enrollment, then compares the row
471 478 // set against the column set. If in multi-process mode, the enrollment and comparison are wrapped in a
... ... @@ -473,47 +480,36 @@ struct AlgorithmCore
473 480  
474 481 // We also need to add Output and progress counting to the algorithm we are building, so we will assign them to
475 482 // two stages of a pipe.
476   - QString joinDesc = "Pipe()";
477   - QScopedPointer<Transform> join(Transform::make(joinDesc, NULL));
  483 + QList<Transform *> compareOutput;
  484 + compareOutput.append(compareRegion.data());
478 485  
479 486 // The output transform takes the metadata memGalleries we set up previously as input, along with the
480 487 // output specification we were passed. Gallery metadata is necessary for some Outputs to function correctly.
481 488 QString outputString = output.flat().isEmpty() ? "Empty" : output.flat();
482 489 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));
  490 + QScopedPointer<Transform> outputTForm(Transform::make(outputRegionDesc,NULL));
  491 + compareOutput.append(outputTForm.data());
486 492  
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());
  493 + // The ProgressCounter transform will simply provide a display about the number of rows completed.
  494 + compareOutput.append(progressCounter.data());
  495 + QScopedPointer<Transform> discard(Transform::make("Discard",NULL));
  496 + compareOutput.append(discard.data());
492 497  
493 498 // With this, we have set up a transform which (optionally) enrolls templates, compares them
494 499 // against a gallery, and outputs them.
495   - join->init();
  500 + Transform *pipeline = br::pipeTransforms(compareOutput);
496 501  
497 502 // Now, we will give that base transform to a stream, which will incrementally read the row gallery
498 503 // 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();
  504 + QScopedPointer<Transform> streamWrapper(br::wrapTransform(pipeline, "Stream(readMode=StreamGallery)"));
506 505  
507 506 // We set up a template containing the rowGallery we want to compare.
508 507 TemplateList rowGalleryTemplate;
509 508 rowGalleryTemplate.append(Template(rowGallery));
510 509 TemplateList outputGallery;
511 510  
512   - // Set up progress counting variables
513   - Globals->currentStep = 0;
514   - Globals->currentProgress = 0;
515   - Globals->totalSteps = rowSize;
516   - Globals->startTime.start();
  511 + // initialize the progress counter
  512 + progressCounter->setPropertyRecursive("totalProgress", QString::number(rowSize));
517 513  
518 514 // Do the actual comparisons
519 515 streamWrapper->projectUpdate(rowGalleryTemplate, outputGallery);
... ... @@ -547,37 +543,39 @@ private:
547 543  
548 544 void init(const QString &description)
549 545 {
550   - if (loadOrExpand(description))
  546 + bool newTForm = false;
  547 +
  548 + if (loadOrExpand(description)) {
  549 + simplifyTransform();
551 550 return;
  551 + }
552 552  
553 553 // check if the description is an abbreviation or model file with additional arguments supplied
554 554 File parsed("."+description);
555 555 if (loadOrExpand(parsed.suffix())) {
556 556 applyAdditionalProperties(parsed, transform.data());
  557 + simplifyTransform();
557 558 return;
558 559 }
559 560  
  561 + //! [Parsing the algorithm description]
560 562 const bool compareTransform = description.contains('!');
561 563 QStringList words = QtUtils::parse(description, compareTransform ? '!' : ':');
562 564  
563 565 if ((words.size() < 1) || (words.size() > 2)) qFatal("Invalid algorithm format.");
564 566  
565   - //! [Parsing the algorithm description]
566   - transformString = words[0];
567   -
568 567 //! [Creating the template generation and comparison methods]
569 568 transform = QSharedPointer<Transform>(Transform::make(words[0], NULL));
  569 + simplifyTransform();
  570 +
570 571 if (words.size() > 1) {
571 572 if (!compareTransform) {
572 573 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();
  574 + comparison = QSharedPointer<Transform>(Transform::make("GalleryCompare", NULL));
  575 + comparison->setPropertyRecursive("distance", QVariant::fromValue(distance.data()));
579 576 }
580   -
  577 + else
  578 + comparison = QSharedPointer<Transform>(Transform::make(words[1], NULL));
581 579 }
582 580 //! [Creating the template generation and comparison methods]
583 581 }
... ...
openbr/core/eval.cpp
... ... @@ -81,6 +81,8 @@ static cv::Mat constructMatchingMask(const cv::Mat &amp;scores, const FileList &amp;targ
81 81 // otherwise, we fail
82 82 else
83 83 qFatal("Unable to construct mask for %d by %d score matrix from %d element query set, and %d element target set ", scores.rows, scores.cols, query.length(), target.length());
  84 +
  85 + return cv::Mat();
84 86 }
85 87  
86 88 float Evaluate(const cv::Mat &scores, const FileList &target, const FileList &query, const QString &csv, int partition)
... ...
openbr/core/qtutils.cpp
... ... @@ -105,7 +105,7 @@ void writeFile(const QString &amp;file, const QStringList &amp;lines)
105 105 if (!f.open(QFile::WriteOnly))
106 106 qFatal("Failed to open %s for writing.", qPrintable(file));
107 107  
108   - foreach (const QString & line, lines)
  108 + foreach (const QString &line, lines)
109 109 f.write((line+"\n").toLocal8Bit() );
110 110  
111 111 f.close();
... ...
openbr/openbr.cpp
... ... @@ -28,7 +28,7 @@
28 28  
29 29 using namespace br;
30 30  
31   -static int partialCopy(const QString & string, char * buffer, int buffer_length)
  31 +static int partialCopy(const QString &string, char *buffer, int buffer_length)
32 32 {
33 33  
34 34 QByteArray byteArray = string.toLocal8Bit();
... ... @@ -109,7 +109,7 @@ float br_eval(const char *simmat, const char *mask, const char *csv)
109 109 return Evaluate(simmat, mask, csv);
110 110 }
111 111  
112   -float br_inplace_eval(const char * simmat, const char *target, const char *query, const char *csv)
  112 +float br_inplace_eval(const char *simmat, const char *target, const char *query, const char *csv)
113 113 {
114 114 return InplaceEval(simmat, target, query, csv);
115 115 }
... ... @@ -119,7 +119,7 @@ void br_eval_classification(const char *predicted_gallery, const char *truth_gal
119 119 EvalClassification(predicted_gallery, truth_gallery, predicted_property, truth_property);
120 120 }
121 121  
122   -void br_eval_clustering(const char *csv, const char *gallery, const char * truth_property)
  122 +void br_eval_clustering(const char *csv, const char *gallery, const char *truth_property)
123 123 {
124 124 EvalClustering(csv, gallery, truth_property);
125 125 }
... ... @@ -178,12 +178,12 @@ void br_make_pairwise_mask(const char *target_input, const char *query_input, co
178 178 BEE::makePairwiseMask(target_input, query_input, mask);
179 179 }
180 180  
181   -int br_most_recent_message(char * buffer, int buffer_length)
  181 +int br_most_recent_message(char *buffer, int buffer_length)
182 182 {
183 183 return partialCopy(Globals->mostRecentMessage, buffer, buffer_length);
184 184 }
185 185  
186   -int br_objects(char * buffer, int buffer_length, const char *abstractions, const char *implementations, bool parameters)
  186 +int br_objects(char *buffer, int buffer_length, const char *abstractions, const char *implementations, bool parameters)
187 187 {
188 188 return partialCopy(br::Context::objects(abstractions, implementations, parameters).join('\n'), buffer, buffer_length);
189 189 }
... ... @@ -240,7 +240,7 @@ void br_read_pipe(const char *pipe, int *argc, char ***argv)
240 240 *argv = rawCharArrayList.data();
241 241 }
242 242  
243   -int br_scratch_path(char * buffer, int buffer_length)
  243 +int br_scratch_path(char *buffer, int buffer_length)
244 244 {
245 245 return partialCopy(Context::scratchPath(), buffer, buffer_length);
246 246 }
... ... @@ -298,9 +298,9 @@ const char *br_version()
298 298 return version.data();
299 299 }
300 300  
301   -void br_slave_process(const char * baseName)
  301 +void br_slave_process(const char *baseName)
302 302 {
303   - WorkerProcess * worker = new WorkerProcess;
  303 + WorkerProcess *worker = new WorkerProcess;
304 304 worker->transform = Globals->algorithm;
305 305 worker->baseName = baseName;
306 306 worker->mainLoop();
... ... @@ -371,7 +371,7 @@ bool br_img_is_empty(br_template tmpl)
371 371 return t->m().empty();
372 372 }
373 373  
374   -int br_get_filename(char * buffer, int buffer_length, br_template tmpl)
  374 +int br_get_filename(char *buffer, int buffer_length, br_template tmpl)
375 375 {
376 376 return partialCopy(reinterpret_cast<Template*>(tmpl)->file.name, buffer, buffer_length);
377 377 }
... ... @@ -382,7 +382,7 @@ void br_set_filename(br_template tmpl, const char *filename)
382 382 t->file.name = filename;
383 383 }
384 384  
385   -int br_get_metadata_string(char * buffer, int buffer_length, br_template tmpl, const char *key)
  385 +int br_get_metadata_string(char *buffer, int buffer_length, br_template tmpl, const char *key)
386 386 {
387 387 Template *t = reinterpret_cast<Template*>(tmpl);
388 388 QVariant qvar = t->file.value(key);
... ...
openbr/openbr_plugin.cpp
... ... @@ -585,16 +585,51 @@ QStringList Object::parameters() const
585 585 return parameters;
586 586 }
587 587  
588   -QStringList Object::arguments() const
  588 +QStringList Object::prunedArguments(bool expanded) const
589 589 {
590 590 QStringList arguments;
591   - for (int i=metaObject()->propertyOffset(); i<metaObject()->propertyCount(); i++)
592   - arguments.append(argument(i));
  591 + QString className = this->metaObject()->className();
  592 + QScopedPointer<Object> shellObject;
  593 +
  594 + if (className.startsWith("br::"))
  595 + className = className.mid(4);
  596 + if (!className.startsWith("."))
  597 + className = "." + className;
  598 +
  599 + if (className.endsWith("Distance")) {
  600 + className.chop(QString("Distance").size());
  601 + shellObject.reset(Factory<Distance>::make(className));
  602 + }
  603 + else if (className.endsWith("Transform")) {
  604 + className.chop(QString("Transform").size());
  605 + shellObject.reset(Factory<Transform>::make(className));
  606 + }
  607 + else if (className.endsWith("Format")) {
  608 + className.chop(QString("Format").size());
  609 + shellObject.reset(Factory<Format>::make(className));
  610 + }
  611 + else if (className.endsWith("Initializer")) {
  612 + className.chop(QString("Initializer").size());
  613 + shellObject.reset(Factory<Initializer>::make(className));
  614 + }
  615 + else if (className.endsWith("Output")) {
  616 + className.chop(QString("Output").size());
  617 + shellObject.reset(Factory<Output>::make(className));
  618 + }
  619 +
  620 + for (int i=firstAvailablePropertyIdx; i<metaObject()->propertyCount(); i++) {
  621 + const char *name = metaObject()->property(i).name();
  622 +
  623 + QVariant defaultVal = shellObject->property(name);
  624 +
  625 + if (defaultVal != property(name))
  626 + arguments.append(name + QString("=") + argument(i, expanded));
  627 + }
593 628  
594 629 return arguments;
595 630 }
596 631  
597   -QString Object::argument(int index) const
  632 +QString Object::argument(int index, bool expanded) const
598 633 {
599 634 if ((index < 0) || (index > metaObject()->propertyCount())) return "";
600 635 const QMetaProperty property = metaObject()->property(index);
... ... @@ -612,19 +647,19 @@ QString Object::argument(int index) const
612 647 strings.append(QString::number(value));
613 648 } else if (type == "QList<br::Transform*>") {
614 649 foreach (Transform *transform, variant.value< QList<Transform*> >())
615   - strings.append(transform->description());
  650 + strings.append(transform->description(expanded));
616 651 } else if (type == "QList<br::Distance*>") {
617 652 foreach (Distance *distance, variant.value< QList<Distance*> >())
618   - strings.append(distance->description());
  653 + strings.append(distance->description(expanded));
619 654 } else {
620 655 qFatal("Unrecognized type: %s", qPrintable(type));
621 656 }
622 657  
623 658 return "[" + strings.join(",") + "]";
624 659 } else if (type == "br::Transform*") {
625   - return variant.value<Transform*>()->description();
  660 + return variant.value<Transform*>()->description(expanded);
626 661 } else if (type == "br::Distance*") {
627   - return variant.value<Distance*>()->description();
  662 + return variant.value<Distance*>()->description(expanded);
628 663 } else if (type == "QStringList") {
629 664 return "[" + variant.toStringList().join(",") + "]";
630 665 }
... ... @@ -632,9 +667,12 @@ QString Object::argument(int index) const
632 667 return variant.toString();
633 668 }
634 669  
635   -QString Object::description() const
  670 +QString Object::description(bool expanded) const
636 671 {
637   - QString argumentString = arguments().join(",");
  672 + QString argumentString = prunedArguments(expanded).join(",");
  673 + if (argumentString.endsWith(","))
  674 + argumentString.chop(1);
  675 +
638 676 return objectName() + (argumentString.isEmpty() ? "" : ("(" + argumentString + ")"));
639 677 }
640 678  
... ... @@ -1292,7 +1330,7 @@ Transform *Transform::make(QString str, QObject *parent)
1292 1330  
1293 1331 Transform *Transform::clone() const
1294 1332 {
1295   - Transform *clone = Factory<Transform>::make(file.flat());
  1333 + Transform *clone = Factory<Transform>::make("."+description(false));
1296 1334 return clone;
1297 1335 }
1298 1336  
... ... @@ -1461,3 +1499,17 @@ void br::applyAdditionalProperties(const File &amp;temp, Transform *target)
1461 1499 target->setPropertyRecursive(i.key(), i.value() );
1462 1500 }
1463 1501 }
  1502 +
  1503 +Transform *br::wrapTransform(Transform *base, const QString &target)
  1504 +{
  1505 + Transform *res = Transform::make(target, NULL);
  1506 + res->setPropertyRecursive("transform", QVariant::fromValue(base));
  1507 + return res;
  1508 +}
  1509 +
  1510 +Transform *br::pipeTransforms(QList<Transform *> &transforms)
  1511 +{
  1512 + Transform *res = Transform::make("Pipe",NULL);
  1513 + res->setPropertyRecursive("transforms", QVariant::fromValue(transforms));
  1514 + return res;
  1515 +}
... ...
openbr/openbr_plugin.h
... ... @@ -593,10 +593,18 @@ public:
593 593 virtual void store(QDataStream &stream) const; /*!< \brief Serialize the object. */
594 594 virtual void load(QDataStream &stream); /*!< \brief Deserialize the object. Default implementation calls init() after deserialization. */
595 595  
  596 + /*!< \brief Serialize an object created via the plugin system, including the string used to build the base object, allowing re-creation of the object without knowledge of its base string*/
  597 + virtual void serialize(QDataStream &stream) const
  598 + {
  599 + stream << description();
  600 + store(stream);
  601 + }
  602 +
596 603 QStringList parameters() const; /*!< \brief A string describing the parameters the object takes. */
597   - QStringList arguments() const; /*!< \brief A string describing the values the object has. */
598   - QString argument(int index) const; /*!< \brief A string value for the argument at the specified index. */
599   - QString description() const; /*!< \brief Returns a string description of the object. */
  604 + QStringList prunedArguments(bool expanded = false) const; /*!< \brief A string describing the values the object has, default valued parameters will not be listed. If expanded is true, all abbreviations and model file names should be replaced with a description of the object generated from those names. */
  605 + QString argument(int index, bool expanded) const; /*!< \brief A string value for the argument at the specified index. */
  606 + virtual QString description(bool expanded = false) const; /*!< \brief Returns a string description of the object. */
  607 +
600 608 void setProperty(const QString &name, QVariant value); /*!< \brief Overload of QObject::setProperty to handle OpenBR data types. */
601 609 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. */
602 610  
... ... @@ -1274,6 +1282,21 @@ public:
1274 1282 */
1275 1283 QList<Transform *> getChildren() const;
1276 1284  
  1285 + static Transform *deserialize(QDataStream &stream)
  1286 + {
  1287 + QString desc;
  1288 + stream >> desc;
  1289 + Transform *res = Transform::make(desc, NULL);
  1290 + res->load(stream);
  1291 + return res;
  1292 + }
  1293 +
  1294 + /*!
  1295 + * \brief Return a pointer to a simplified version of this transform (if possible). Transforms which are only active during training should remove
  1296 + * themselves by either returning their child transforms (where relevant) or returning NULL. Set newTransform to true if the transform returned is newly allocated.
  1297 + */
  1298 + virtual Transform * simplify(bool & newTransform) { newTransform = false; return this; }
  1299 +
1277 1300 protected:
1278 1301 Transform(bool independent = true, bool trainable = true); /*!< \brief Construct a transform. */
1279 1302 inline Transform *make(const QString &description) { return make(description, this); } /*!< \brief Make a subtransform. */
... ...
openbr/plugins/distance.cpp
... ... @@ -251,6 +251,7 @@ private:
251 251 default:
252 252 qFatal("Invalid operation.");
253 253 }
  254 + return 0;
254 255 }
255 256  
256 257 void store(QDataStream &stream) const
... ... @@ -460,13 +461,12 @@ BR_REGISTER(Distance, SumDistance)
460 461 class GalleryCompareTransform : public Transform
461 462 {
462 463 Q_OBJECT
463   - Q_PROPERTY(QString distanceAlgorithm READ get_distanceAlgorithm WRITE set_distanceAlgorithm RESET reset_distanceAlgorithm STORED false)
  464 + Q_PROPERTY(br::Distance *distance READ get_distance WRITE set_distance RESET reset_distance STORED true)
464 465 Q_PROPERTY(QString galleryName READ get_galleryName WRITE set_galleryName RESET reset_galleryName STORED false)
465   - BR_PROPERTY(QString, distanceAlgorithm, "")
  466 + BR_PROPERTY(br::Distance*, distance, NULL)
466 467 BR_PROPERTY(QString, galleryName, "")
467 468  
468 469 TemplateList gallery;
469   - QSharedPointer<Distance> distance;
470 470  
471 471 void project(const Template &src, Template &dst) const
472 472 {
... ... @@ -480,16 +480,29 @@ class GalleryCompareTransform : public Transform
480 480  
481 481 void init()
482 482 {
483   - if (!galleryName.isEmpty()) {
  483 + if (!galleryName.isEmpty())
484 484 gallery = TemplateList::fromGallery(galleryName);
485   - }
486   - if (!distanceAlgorithm.isEmpty())
487   - {
488   - distance = Distance::fromAlgorithm(distanceAlgorithm);
489   - }
490 485 }
  486 +
  487 + void train(const TemplateList &data)
  488 + {
  489 + gallery = data;
  490 + }
  491 +
  492 + void store(QDataStream &stream) const
  493 + {
  494 + br::Object::store(stream);
  495 + stream << gallery;
  496 + }
  497 +
  498 + void load(QDataStream &stream)
  499 + {
  500 + br::Object::load(stream);
  501 + stream >> gallery;
  502 + }
  503 +
491 504 public:
492   - GalleryCompareTransform() : Transform(false, false) {}
  505 + GalleryCompareTransform() : Transform(false, true) {}
493 506 };
494 507  
495 508 BR_REGISTER(Transform, GalleryCompareTransform)
... ...
openbr/plugins/frames.cpp
... ... @@ -37,7 +37,7 @@ private:
37 37 dst.append(out);
38 38 }
39 39  
40   - void finalize(TemplateList & output)
  40 + void finalize(TemplateList &output)
41 41 {
42 42 (void) output;
43 43 buffer.clear();
... ...
openbr/plugins/independent.cpp
... ... @@ -94,6 +94,12 @@ class DownsampleTrainingTransform : public Transform
94 94 BR_PROPERTY(QStringList, subjects, QStringList())
95 95  
96 96  
  97 + Transform *simplify(bool &newTForm)
  98 + {
  99 + Transform *res = transform->simplify(newTForm);
  100 + return res;
  101 + }
  102 +
97 103 void project(const Template &src, Template &dst) const
98 104 {
99 105 transform->project(src,dst);
... ... @@ -126,6 +132,10 @@ class IndependentTransform : public MetaTransform
126 132  
127 133 QList<Transform*> transforms;
128 134  
  135 + QString description(bool expanded) const
  136 + {
  137 + return transform->description(expanded);
  138 + }
129 139  
130 140 bool setPropertyRecursive(const QString &name, QVariant value)
131 141 {
... ... @@ -141,6 +151,54 @@ class IndependentTransform : public MetaTransform
141 151 return true;
142 152 }
143 153  
  154 + Transform *simplify(bool &newTransform)
  155 + {
  156 + newTransform = false;
  157 + bool newChild = false;
  158 + Transform *temp = transform->simplify(newChild);
  159 + if (temp == transform) {
  160 + return this;
  161 + }
  162 + IndependentTransform* indep = new IndependentTransform();
  163 + indep->transform = temp;
  164 +
  165 + bool subInd = false;
  166 + IndependentTransform *test = dynamic_cast<IndependentTransform *> (temp);
  167 + if (test) {
  168 + // child was independent? this changes things...
  169 + subInd = true;
  170 + indep->transform = test->transform;
  171 + for (int i=0; i < transforms.size(); i++) {
  172 + bool newThing = false;
  173 + IndependentTransform *probe = dynamic_cast<IndependentTransform *> (transforms[i]->simplify(newThing));
  174 + indep->transforms.append(probe->transform);
  175 + if (newThing)
  176 + probe->setParent(indep);
  177 + }
  178 + indep->file = indep->transform->file;
  179 + indep->trainable = indep->transform->trainable;
  180 + indep->setObjectName(indep->transform->objectName());
  181 +
  182 + return indep;
  183 + }
  184 +
  185 + if (newChild)
  186 + indep->transform->setParent(indep);
  187 +
  188 + for (int i=0; i < transforms.size();i++) {
  189 + bool subTform = false;
  190 + indep->transforms.append(transforms[i]->simplify(subTform));
  191 + if (subTform)
  192 + indep->transforms[i]->setParent(indep);
  193 + }
  194 +
  195 + indep->file = indep->transform->file;
  196 + indep->trainable = indep->transform->trainable;
  197 + indep->setObjectName(indep->transform->objectName());
  198 +
  199 + return indep;
  200 + }
  201 +
144 202 void init()
145 203 {
146 204 transforms.clear();
... ...
openbr/plugins/meta.cpp
... ... @@ -482,17 +482,32 @@ 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 transformString READ get_transformString WRITE set_transformString RESET reset_transformString 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, transformString, "Identity")
488 488 BR_PROPERTY(QString, fileName, QString())
489 489  
  490 +public:
490 491 Transform *transform;
491 492 QString baseName;
492 493  
493   -public:
494 494 LoadStoreTransform() : transform(NULL) {}
495 495  
  496 + QString description(bool expanded = false) const
  497 + {
  498 + if (expanded) {
  499 + QString res = transform->description(expanded);
  500 + return res;
  501 + }
  502 + return br::Object::description(expanded);
  503 + }
  504 +
  505 + Transform *simplify(bool &newTForm)
  506 + {
  507 + Transform *res = transform->simplify(newTForm);
  508 + return res;
  509 + }
  510 +
496 511 bool setPropertyRecursive(const QString &name, QVariant value)
497 512 {
498 513 if (br::Object::setPropertyRecursive(name, value))
... ... @@ -500,13 +515,16 @@ public:
500 515 return transform->setPropertyRecursive(name, value);
501 516 }
502 517 private:
  518 +
503 519 void init()
504 520 {
505 521 if (transform != NULL) return;
506   - if (fileName.isEmpty()) baseName = QRegExp("^[a-zA-Z0-9]+$").exactMatch(description) ? description : QtUtils::shortTextHash(description);
  522 + if (fileName.isEmpty()) baseName = QRegExp("^[a-zA-Z0-9]+$").exactMatch(transformString) ? transformString : QtUtils::shortTextHash(transformString);
507 523 else baseName = fileName;
508   - if (!tryLoad()) transform = make(description);
509   - else trainable = false;
  524 + if (!tryLoad())
  525 + transform = make(transformString);
  526 + else
  527 + trainable = false;
510 528 }
511 529  
512 530 bool timeVarying() const
... ... @@ -524,7 +542,7 @@ private:
524 542 qDebug("Storing %s", qPrintable(baseName));
525 543 QByteArray byteArray;
526 544 QDataStream stream(&byteArray, QFile::WriteOnly);
527   - stream << description;
  545 + stream << transform->description();
528 546 transform->store(stream);
529 547 QtUtils::writeFile(baseName, byteArray, -1);
530 548 }
... ... @@ -570,8 +588,8 @@ private:
570 588 QByteArray data;
571 589 QtUtils::readFile(file, data, true);
572 590 QDataStream stream(&data, QFile::ReadOnly);
573   - stream >> description;
574   - transform = Transform::make(description);
  591 + stream >> transformString;
  592 + transform = Transform::make(transformString);
575 593 transform->load(stream);
576 594 return true;
577 595 }
... ...
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/openbr_internal.h
... ... @@ -201,6 +201,33 @@ public:
201 201 this->trainable = transform->trainable;
202 202 }
203 203  
  204 + virtual Transform *simplify(bool &newTransform)
  205 + {
  206 + newTransform = false;
  207 + bool newChild = false;
  208 + Transform *temp = transform->simplify(newTransform);
  209 + if (temp == transform)
  210 + return this;
  211 +
  212 + if (!temp)
  213 + return NULL;
  214 +
  215 + // else make a copy to point at the new transform
  216 + Transform *child = transform;
  217 + transform = NULL;
  218 + WrapperTransform *output = dynamic_cast<WrapperTransform *>(Transform::make(description(), NULL));
  219 + transform = child;
  220 +
  221 + output->transform = temp;
  222 +
  223 + if (newChild)
  224 + temp->setParent(output);
  225 +
  226 + newTransform = true;
  227 + return output;
  228 + }
  229 +
  230 +
204 231 bool setPropertyRecursive(const QString &name, QVariant value)
205 232 {
206 233 if (br::Object::setPropertyRecursive(name, value))
... ... @@ -212,6 +239,34 @@ public:
212 239 }
213 240 return false;
214 241 }
  242 +
  243 + Transform *smartCopy(bool &newTransform)
  244 + {
  245 + if (!timeVarying()) {
  246 + newTransform = false;
  247 + return this;
  248 + }
  249 + newTransform = true;
  250 + Transform *temp = transform;
  251 + transform = NULL;
  252 + WrapperTransform *output = dynamic_cast<WrapperTransform *>(Transform::make(description(), NULL));
  253 + transform = temp;
  254 +
  255 + if (output == NULL)
  256 + qFatal("Dynamic cast failed!");
  257 +
  258 + bool newItem = false;
  259 + Transform *maybe_copy = transform->smartCopy(newItem);
  260 + if (newItem)
  261 + maybe_copy->setParent(output);
  262 + output->transform = maybe_copy;
  263 +
  264 + output->file = this->file;
  265 + output->init();
  266 +
  267 + return output;
  268 + }
  269 +
215 270 };
216 271  
217 272 /*!
... ... @@ -270,21 +325,10 @@ public:
270 325 }
271 326 newTransform = true;
272 327  
273   - QString name = metaObject()->className();
274   -
275   - name.replace("Transform","");
276   - name += "([]";
277   -
278   - QStringList arguments = this->arguments();
279   - if (!arguments.isEmpty()) {
280   - name += ",";
281   - name += this->arguments().join(",");
282   - }
283   -
284   - name += ")";
285   - name.replace("br::","");
286   -
287   - CompositeTransform *output = dynamic_cast<CompositeTransform *>(Transform::make(name, NULL));
  328 + QList<Transform *> temp = transforms;
  329 + transforms = QList<Transform *>();
  330 + CompositeTransform *output = dynamic_cast<CompositeTransform *>(Transform::make(description(), NULL));
  331 + transforms = temp;
288 332  
289 333 if (output == NULL)
290 334 qFatal("Dynamic cast failed!");
... ... @@ -303,6 +347,51 @@ public:
303 347 return output;
304 348 }
305 349  
  350 + virtual Transform *simplify(bool &newTransform)
  351 + {
  352 + newTransform = false;
  353 + QList<Transform *> newTransforms;
  354 + bool anyNew = false;
  355 +
  356 + QList<bool> newChildren;
  357 + for (int i=0; i < transforms.size();i++)
  358 + {
  359 + bool newChild = false;
  360 + Transform *temp = transforms[i]->simplify(newChild);
  361 + if (temp == NULL) {
  362 + anyNew = true;
  363 + continue;
  364 + }
  365 + newTransforms.append(temp);
  366 + newChildren.append(newChild);
  367 + if (temp != transforms[i])
  368 + anyNew = true;
  369 + }
  370 +
  371 + if (newTransforms.empty() )
  372 + return NULL;
  373 +
  374 + if (!anyNew)
  375 + return this;
  376 +
  377 + // make a copy of the current object, with empty transforms
  378 + QList<Transform *> children = transforms;
  379 + transforms = QList<Transform *> ();
  380 + CompositeTransform *output = dynamic_cast<CompositeTransform *>(Transform::make(description(false), NULL));
  381 + transforms = children;
  382 +
  383 + output->transforms = newTransforms;
  384 + for (int i=0;i < newChildren.size();i++)
  385 + {
  386 + if (newChildren[i])
  387 + output->transforms[i]->setParent(output);
  388 + }
  389 + output->init();
  390 +
  391 + newTransform = true;
  392 + return output;
  393 + }
  394 +
306 395 bool setPropertyRecursive(const QString &name, QVariant value)
307 396 {
308 397 if (br::Object::setPropertyRecursive(name, value))
... ... @@ -388,6 +477,10 @@ public:
388 477  
389 478 void applyAdditionalProperties(const File &temp, Transform *target);
390 479  
  480 +Transform *wrapTransform(Transform *base, const QString &target);
  481 +
  482 +Transform *pipeTransforms(QList<Transform *> &transforms);
  483 +
391 484 }
392 485  
393 486 #endif // OPENBR_INTERNAL_H
... ...
openbr/plugins/pp5.cpp
... ... @@ -450,8 +450,9 @@ class PP5GalleryTransform: public UntrainableMetaTransform
450 450  
451 451 ppr_gallery_type target;
452 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 {
456 457 TemplateList temp, output;
457 458 temp.append(src);
... ... @@ -460,7 +461,7 @@ class PP5GalleryTransform: public UntrainableMetaTransform
460 461 dst = output[0];
461 462 }
462 463  
463   - void project(const TemplateList & src, TemplateList & dst) const
  464 + void project(const TemplateList &src, TemplateList &dst) const
464 465 {
465 466 dst.clear();
466 467 QList<int> queryIDs;
... ... @@ -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;
  511 + }
  512 +
  513 + void store(QDataStream &stream) const
  514 + {
  515 + br::Object::store(stream);
  516 + stream << gallery;
  517 + }
  518 +
  519 + void load(QDataStream &stream)
  520 + {
  521 + br::Object::load(stream);
  522 + stream >> gallery;
  523 + init();
502 524 }
  525 +
503 526 };
504 527  
505 528 BR_REGISTER(Transform, PP5GalleryTransform)
... ...
openbr/plugins/process.cpp
... ... @@ -21,7 +21,7 @@ class CommunicationManager : public QObject
21 21 {
22 22 Q_OBJECT
23 23 public:
24   - QThread * basis;
  24 + QThread *basis;
25 25 CommunicationManager()
26 26 {
27 27 basis = new QThread;
... ... @@ -294,7 +294,7 @@ public:
294 294 QString serverName;
295 295 QString remoteName;
296 296  
297   - QLocalSocket * inbound;
  297 + QLocalSocket *inbound;
298 298 QLocalSocket outbound;
299 299 QLocalServer server;
300 300  
... ... @@ -305,13 +305,11 @@ 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  
314   - void connectToRemote(const QString & remoteName)
  312 + void connectToRemote(const QString &remoteName)
315 313 {
316 314 emit pulseOutboundConnect(remoteName);
317 315  
... ... @@ -330,7 +328,7 @@ public:
330 328  
331 329  
332 330 template<typename T>
333   - bool readData(T & input)
  331 + bool readData(T &input)
334 332 {
335 333 emit pulseReadSerialized();
336 334 QDataStream deserializer(readArray);
... ... @@ -338,8 +336,18 @@ 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   - bool sendData(const T & output)
  350 + bool sendData(const T &output)
343 351 {
344 352 QBuffer buffer;
345 353 buffer.open(QBuffer::ReadWrite);
... ... @@ -378,7 +386,7 @@ class EnrollmentWorker : public QObject
378 386 {
379 387 Q_OBJECT
380 388 public:
381   - CommunicationManager * comm;
  389 + CommunicationManager *comm;
382 390 QString name;
383 391  
384 392 ~EnrollmentWorker()
... ... @@ -389,10 +397,10 @@ public:
389 397 delete comm;
390 398 }
391 399  
392   - br::Transform * transform;
  400 + br::Transform *transform;
393 401  
394 402 public:
395   - void connections(const QString & baseName)
  403 + void connections(const QString &baseName)
396 404 {
397 405 comm = new CommunicationManager();
398 406 name = baseName;
... ... @@ -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,99 @@ 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;
497 523  
498   - void projectUpdate(const TemplateList &src, TemplateList &dst)
  524 + Transform *smartCopy(bool &newTransform)
  525 + {
  526 + newTransform = false;
  527 + return this;
  528 + }
  529 +
  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);
  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.
  581 + argumentList.append("-quiet");
536 582 argumentList.append("-algorithm");
537   - argumentList.append(transform);
  583 + argumentList.append("Identity");
538 584 if (!Globals->path.isEmpty()) {
539 585 argumentList.append("-path");
540 586 argumentList.append(Globals->path);
... ... @@ -544,36 +590,25 @@ class ProcessWrapperTransform : public TimeVaryingTransform
544 590 argumentList.append("-slave");
545 591 argumentList.append(baseKey);
546 592  
547   - comm->key = "master_"+baseKey.mid(1,5);
  593 + data->comm.key = "master_"+baseKey.mid(1,5);
548 594  
549   - comm->startServer(baseKey+"_master");
550   - workerProcess.startProcess(argumentList);
551   - comm->waitForInbound();
552   - comm->connectToRemote(baseKey+"_worker");
553   - }
  595 + data->comm.startServer(baseKey+"_master");
554 596  
555   - bool timeVarying() const {
556   - return false;
  597 + data->proc.startProcess(argumentList);
  598 + data->comm.waitForInbound();
  599 + data->comm.connectToRemote(baseKey+"_worker");
  600 +
  601 + transmitTForm(&(data->comm));
557 602 }
558 603  
559   - ~ProcessWrapperTransform()
  604 + bool timeVarying() const
560 605 {
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   - }
  606 + return false;
572 607 }
573 608  
574 609 public:
575 610 bool processActive;
576   - ProcessWrapperTransform() : TimeVaryingTransform(false,false) { processActive = false; }
  611 + ProcessWrapperTransform() : WrapperTransform(false) { processActive = false; }
577 612 };
578 613  
579 614 BR_REGISTER(Transform, ProcessWrapperTransform)
... ...
openbr/plugins/validate.cpp
... ... @@ -7,7 +7,7 @@
7 7 namespace br
8 8 {
9 9  
10   -static void _train(Transform * transform, TemplateList data) // think data has to be a copy -cao
  10 +static void _train(Transform *transform, TemplateList data) // think data has to be a copy -cao
11 11 {
12 12 transform->train(data);
13 13 }
... ...
1   -Subproject commit bcbff8c485f19daddb2e6b2abd5a505ed8c1e526
  1 +Subproject commit 79938fe401faafead086b4711dd0b8f898a7a21e
... ...