Commit 36d2607d60d85383c75edfd11a401aa4b8baece7
Merge pull request #216 from biometrics/serialization_update
Better support for serializing algorithms in memory
Showing
17 changed files
with
573 additions
and
254 deletions
CMakeLists.txt
| @@ -121,7 +121,7 @@ else() | @@ -121,7 +121,7 @@ else() | ||
| 121 | set(CMAKE_EXE_LINKER_FLAGS "-Wl,--enable-auto-import") # Fixes a linker warning | 121 | set(CMAKE_EXE_LINKER_FLAGS "-Wl,--enable-auto-import") # Fixes a linker warning |
| 122 | set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--enable-auto-import") | 122 | set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--enable-auto-import") |
| 123 | elseif(MSVC) | 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 | endif() | 125 | endif() |
| 126 | endif() | 126 | endif() |
| 127 | 127 |
openbr/core/core.cpp
| @@ -23,24 +23,36 @@ | @@ -23,24 +23,36 @@ | ||
| 23 | 23 | ||
| 24 | namespace br { | 24 | namespace br { |
| 25 | 25 | ||
| 26 | +void noDelete(Transform *target) | ||
| 27 | +{ | ||
| 28 | + (void) target; | ||
| 29 | +} | ||
| 30 | + | ||
| 26 | struct AlgorithmCore | 31 | struct AlgorithmCore |
| 27 | { | 32 | { |
| 33 | + enum CompareMode | ||
| 34 | + { | ||
| 35 | + None, | ||
| 36 | + DistanceCompare, | ||
| 37 | + TransformCompare, | ||
| 38 | + }; | ||
| 39 | + | ||
| 28 | QSharedPointer<Transform> transform; | 40 | QSharedPointer<Transform> transform; |
| 41 | + QSharedPointer<Transform> simplifiedTransform; | ||
| 42 | + QSharedPointer<Transform> comparison; | ||
| 29 | QSharedPointer<Distance> distance; | 43 | QSharedPointer<Distance> distance; |
| 30 | - QString galleryCompareString; | ||
| 31 | - | ||
| 32 | - QString transformString; | ||
| 33 | - QString distanceString; | 44 | + QSharedPointer<Transform> progressCounter; |
| 34 | 45 | ||
| 35 | AlgorithmCore(const QString &name) | 46 | AlgorithmCore(const QString &name) |
| 36 | { | 47 | { |
| 37 | this->name = name; | 48 | this->name = name; |
| 38 | init(name); | 49 | init(name); |
| 50 | + progressCounter = QSharedPointer<Transform>(Transform::make("ProgressCounter", NULL)); | ||
| 39 | } | 51 | } |
| 40 | 52 | ||
| 41 | bool isClassifier() const | 53 | bool isClassifier() const |
| 42 | { | 54 | { |
| 43 | - return distance.isNull(); | 55 | + return comparison.isNull(); |
| 44 | } | 56 | } |
| 45 | 57 | ||
| 46 | void train(const File &input, const QString &model) | 58 | void train(const File &input, const QString &model) |
| @@ -48,15 +60,7 @@ struct AlgorithmCore | @@ -48,15 +60,7 @@ struct AlgorithmCore | ||
| 48 | qDebug("Training on %s%s", qPrintable(input.flat()), | 60 | qDebug("Training on %s%s", qPrintable(input.flat()), |
| 49 | model.isEmpty() ? "" : qPrintable(" to " + model)); | 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 | TemplateList data(TemplateList::fromGallery(input)); | 64 | TemplateList data(TemplateList::fromGallery(input)); |
| 61 | 65 | ||
| 62 | if (transform.isNull()) qFatal("Null transform."); | 66 | if (transform.isNull()) qFatal("Null transform."); |
| @@ -65,14 +69,14 @@ struct AlgorithmCore | @@ -65,14 +69,14 @@ struct AlgorithmCore | ||
| 65 | Globals->startTime.start(); | 69 | Globals->startTime.start(); |
| 66 | 70 | ||
| 67 | qDebug("Training Enrollment"); | 71 | qDebug("Training Enrollment"); |
| 68 | - downcast->train(data); | 72 | + trainingWrapper->train(data); |
| 69 | 73 | ||
| 70 | if (!distance.isNull()) { | 74 | if (!distance.isNull()) { |
| 71 | if (Globals->crossValidate > 0) | 75 | if (Globals->crossValidate > 0) |
| 72 | for (int i=data.size()-1; i>=0; i--) if (data[i].file.get<bool>("allPartitions",false)) data.removeAt(i); | 76 | for (int i=data.size()-1; i>=0; i--) if (data[i].file.get<bool>("allPartitions",false)) data.removeAt(i); |
| 73 | 77 | ||
| 74 | qDebug("Projecting Enrollment"); | 78 | qDebug("Projecting Enrollment"); |
| 75 | - downcast->projectUpdate(data,data); | 79 | + trainingWrapper->projectUpdate(data,data); |
| 76 | 80 | ||
| 77 | qDebug("Training Comparison"); | 81 | qDebug("Training Comparison"); |
| 78 | distance->train(data); | 82 | distance->train(data); |
| @@ -84,6 +88,18 @@ struct AlgorithmCore | @@ -84,6 +88,18 @@ struct AlgorithmCore | ||
| 84 | } | 88 | } |
| 85 | 89 | ||
| 86 | qDebug("Training Time: %s", qPrintable(QtUtils::toTime(Globals->startTime.elapsed()/1000.0f))); | 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 | void store(const QString &model) const | 105 | void store(const QString &model) const |
| @@ -93,11 +109,21 @@ struct AlgorithmCore | @@ -93,11 +109,21 @@ struct AlgorithmCore | ||
| 93 | QDataStream out(&data, QFile::WriteOnly); | 109 | QDataStream out(&data, QFile::WriteOnly); |
| 94 | 110 | ||
| 95 | // Serialize algorithm to stream | 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 | // Compress and save to file | 128 | // Compress and save to file |
| 103 | QtUtils::writeFile(model, data, -1); | 129 | QtUtils::writeFile(model, data, -1); |
| @@ -113,10 +139,21 @@ struct AlgorithmCore | @@ -113,10 +139,21 @@ struct AlgorithmCore | ||
| 113 | QDataStream in(&data, QFile::ReadOnly); | 139 | QDataStream in(&data, QFile::ReadOnly); |
| 114 | 140 | ||
| 115 | // Load algorithm | 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 | File getMemoryGallery(const File &file) const | 159 | File getMemoryGallery(const File &file) const |
| @@ -148,54 +185,38 @@ struct AlgorithmCore | @@ -148,54 +185,38 @@ struct AlgorithmCore | ||
| 148 | Gallery *temp = Gallery::make(input); | 185 | Gallery *temp = Gallery::make(input); |
| 149 | qint64 total = temp->totalSize(); | 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 | TemplateList data, output; | 210 | TemplateList data, output; |
| 194 | data.append(input); | 211 | data.append(input); |
| 195 | - wrapper->projectUpdate(data, output); | 212 | + progressCounter->setPropertyRecursive("totalProgress", QString::number(total)); |
| 213 | + stream->projectUpdate(data, output); | ||
| 196 | 214 | ||
| 197 | files.append(output.files()); | 215 | files.append(output.files()); |
| 198 | 216 | ||
| 217 | + if (multiProcess) | ||
| 218 | + delete enroll; | ||
| 219 | + | ||
| 199 | return files; | 220 | return files; |
| 200 | } | 221 | } |
| 201 | 222 | ||
| @@ -364,7 +385,6 @@ struct AlgorithmCore | @@ -364,7 +385,6 @@ struct AlgorithmCore | ||
| 364 | targetMetadata = FileList::fromGallery(targetGallery, true); | 385 | targetMetadata = FileList::fromGallery(targetGallery, true); |
| 365 | queryMetadata = FileList::fromGallery(queryGallery, true); | 386 | queryMetadata = FileList::fromGallery(queryGallery, true); |
| 366 | 387 | ||
| 367 | - | ||
| 368 | // 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) | 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 | transposeMode = targetMetadata.size() > queryMetadata.size(); | 389 | transposeMode = targetMetadata.size() > queryMetadata.size(); |
| 370 | 390 | ||
| @@ -389,12 +409,12 @@ struct AlgorithmCore | @@ -389,12 +409,12 @@ struct AlgorithmCore | ||
| 389 | // simple make sure the enrolled data is stored in a memGallery, but in multi-process mode we save the enrolled | 409 | // simple make sure the enrolled data is stored in a memGallery, but in multi-process mode we save the enrolled |
| 390 | // data to disk (as a .gal file) so that each worker process can read it without re-doing enrollment. | 410 | // data to disk (as a .gal file) so that each worker process can read it without re-doing enrollment. |
| 391 | File colEnrolledGallery = colGallery; | 411 | File colEnrolledGallery = colGallery; |
| 392 | - QString targetExtension = multiProcess ? "gal" : "mem"; | 412 | + QString targetExtension = "mem"; |
| 393 | 413 | ||
| 394 | // If the column gallery is not already of the appropriate type, we need to do something | 414 | // If the column gallery is not already of the appropriate type, we need to do something |
| 395 | if (colGallery.suffix() != targetExtension) { | 415 | if (colGallery.suffix() != targetExtension) { |
| 396 | // Build the name of a gallery containing the enrolled data, of the appropriate type. | 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 | // Check if we have to do real enrollment, and not just convert the gallery's type. | 419 | // Check if we have to do real enrollment, and not just convert the gallery's type. |
| 400 | if (!(QStringList() << "gal" << "template" << "mem").contains(colGallery.suffix())) | 420 | if (!(QStringList() << "gal" << "template" << "mem").contains(colGallery.suffix())) |
| @@ -435,37 +455,24 @@ struct AlgorithmCore | @@ -435,37 +455,24 @@ struct AlgorithmCore | ||
| 435 | // The actual comparison step is done by a GalleryCompare transform, which has a Distance, and a gallery as data. | 455 | // The actual comparison step is done by a GalleryCompare transform, which has a Distance, and a gallery as data. |
| 436 | // Incoming templates are compared against the templates in the gallery, and the output is the resulting score | 456 | // Incoming templates are compared against the templates in the gallery, and the output is the resulting score |
| 437 | // vector. | 457 | // vector. |
| 458 | + TemplateList tlist = TemplateList::fromGallery(colEnrolledGallery); | ||
| 459 | + comparison->train(tlist); | ||
| 460 | + comparison->setPropertyRecursive("galleryName",""); | ||
| 461 | + | ||
| 438 | QString compareRegionDesc; | 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 | // At this point, compareRegion is a transform, which optionally does enrollment, then compares the row | 477 | // At this point, compareRegion is a transform, which optionally does enrollment, then compares the row |
| 471 | // set against the column set. If in multi-process mode, the enrollment and comparison are wrapped in a | 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,47 +480,36 @@ struct AlgorithmCore | ||
| 473 | 480 | ||
| 474 | // We also need to add Output and progress counting to the algorithm we are building, so we will assign them to | 481 | // We also need to add Output and progress counting to the algorithm we are building, so we will assign them to |
| 475 | // two stages of a pipe. | 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 | // The output transform takes the metadata memGalleries we set up previously as input, along with the | 486 | // The output transform takes the metadata memGalleries we set up previously as input, along with the |
| 480 | // output specification we were passed. Gallery metadata is necessary for some Outputs to function correctly. | 487 | // output specification we were passed. Gallery metadata is necessary for some Outputs to function correctly. |
| 481 | QString outputString = output.flat().isEmpty() ? "Empty" : output.flat(); | 488 | QString outputString = output.flat().isEmpty() ? "Empty" : output.flat(); |
| 482 | QString outputRegionDesc = "Output("+ outputString +"," + targetGallery.flat() +"," + queryGallery.flat() + ","+ QString::number(transposeMode ? 1 : 0) + ")"; | 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 | // With this, we have set up a transform which (optionally) enrolls templates, compares them | 498 | // With this, we have set up a transform which (optionally) enrolls templates, compares them |
| 494 | // against a gallery, and outputs them. | 499 | // against a gallery, and outputs them. |
| 495 | - join->init(); | 500 | + Transform *pipeline = br::pipeTransforms(compareOutput); |
| 496 | 501 | ||
| 497 | // Now, we will give that base transform to a stream, which will incrementally read the row gallery | 502 | // Now, we will give that base transform to a stream, which will incrementally read the row gallery |
| 498 | // and pass the transforms it reads through the base algorithm. | 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 | // We set up a template containing the rowGallery we want to compare. | 506 | // We set up a template containing the rowGallery we want to compare. |
| 508 | TemplateList rowGalleryTemplate; | 507 | TemplateList rowGalleryTemplate; |
| 509 | rowGalleryTemplate.append(Template(rowGallery)); | 508 | rowGalleryTemplate.append(Template(rowGallery)); |
| 510 | TemplateList outputGallery; | 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 | // Do the actual comparisons | 514 | // Do the actual comparisons |
| 519 | streamWrapper->projectUpdate(rowGalleryTemplate, outputGallery); | 515 | streamWrapper->projectUpdate(rowGalleryTemplate, outputGallery); |
| @@ -547,37 +543,39 @@ private: | @@ -547,37 +543,39 @@ private: | ||
| 547 | 543 | ||
| 548 | void init(const QString &description) | 544 | void init(const QString &description) |
| 549 | { | 545 | { |
| 550 | - if (loadOrExpand(description)) | 546 | + bool newTForm = false; |
| 547 | + | ||
| 548 | + if (loadOrExpand(description)) { | ||
| 549 | + simplifyTransform(); | ||
| 551 | return; | 550 | return; |
| 551 | + } | ||
| 552 | 552 | ||
| 553 | // check if the description is an abbreviation or model file with additional arguments supplied | 553 | // check if the description is an abbreviation or model file with additional arguments supplied |
| 554 | File parsed("."+description); | 554 | File parsed("."+description); |
| 555 | if (loadOrExpand(parsed.suffix())) { | 555 | if (loadOrExpand(parsed.suffix())) { |
| 556 | applyAdditionalProperties(parsed, transform.data()); | 556 | applyAdditionalProperties(parsed, transform.data()); |
| 557 | + simplifyTransform(); | ||
| 557 | return; | 558 | return; |
| 558 | } | 559 | } |
| 559 | 560 | ||
| 561 | + //! [Parsing the algorithm description] | ||
| 560 | const bool compareTransform = description.contains('!'); | 562 | const bool compareTransform = description.contains('!'); |
| 561 | QStringList words = QtUtils::parse(description, compareTransform ? '!' : ':'); | 563 | QStringList words = QtUtils::parse(description, compareTransform ? '!' : ':'); |
| 562 | 564 | ||
| 563 | if ((words.size() < 1) || (words.size() > 2)) qFatal("Invalid algorithm format."); | 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 | //! [Creating the template generation and comparison methods] | 567 | //! [Creating the template generation and comparison methods] |
| 569 | transform = QSharedPointer<Transform>(Transform::make(words[0], NULL)); | 568 | transform = QSharedPointer<Transform>(Transform::make(words[0], NULL)); |
| 569 | + simplifyTransform(); | ||
| 570 | + | ||
| 570 | if (words.size() > 1) { | 571 | if (words.size() > 1) { |
| 571 | if (!compareTransform) { | 572 | if (!compareTransform) { |
| 572 | distance = QSharedPointer<Distance>(Distance::make(words[1], NULL)); | 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 | //! [Creating the template generation and comparison methods] | 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 &scores, const FileList &targ | @@ -81,6 +81,8 @@ static cv::Mat constructMatchingMask(const cv::Mat &scores, const FileList &targ | ||
| 81 | // otherwise, we fail | 81 | // otherwise, we fail |
| 82 | else | 82 | else |
| 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()); | 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 | float Evaluate(const cv::Mat &scores, const FileList &target, const FileList &query, const QString &csv, int partition) | 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 &file, const QStringList &lines) | @@ -105,7 +105,7 @@ void writeFile(const QString &file, const QStringList &lines) | ||
| 105 | if (!f.open(QFile::WriteOnly)) | 105 | if (!f.open(QFile::WriteOnly)) |
| 106 | qFatal("Failed to open %s for writing.", qPrintable(file)); | 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 | f.write((line+"\n").toLocal8Bit() ); | 109 | f.write((line+"\n").toLocal8Bit() ); |
| 110 | 110 | ||
| 111 | f.close(); | 111 | f.close(); |
openbr/openbr.cpp
| @@ -28,7 +28,7 @@ | @@ -28,7 +28,7 @@ | ||
| 28 | 28 | ||
| 29 | using namespace br; | 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 | QByteArray byteArray = string.toLocal8Bit(); | 34 | QByteArray byteArray = string.toLocal8Bit(); |
| @@ -109,7 +109,7 @@ float br_eval(const char *simmat, const char *mask, const char *csv) | @@ -109,7 +109,7 @@ float br_eval(const char *simmat, const char *mask, const char *csv) | ||
| 109 | return Evaluate(simmat, mask, csv); | 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 | return InplaceEval(simmat, target, query, csv); | 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,7 +119,7 @@ void br_eval_classification(const char *predicted_gallery, const char *truth_gal | ||
| 119 | EvalClassification(predicted_gallery, truth_gallery, predicted_property, truth_property); | 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 | EvalClustering(csv, gallery, truth_property); | 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,12 +178,12 @@ void br_make_pairwise_mask(const char *target_input, const char *query_input, co | ||
| 178 | BEE::makePairwiseMask(target_input, query_input, mask); | 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 | return partialCopy(Globals->mostRecentMessage, buffer, buffer_length); | 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 | return partialCopy(br::Context::objects(abstractions, implementations, parameters).join('\n'), buffer, buffer_length); | 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,7 +240,7 @@ void br_read_pipe(const char *pipe, int *argc, char ***argv) | ||
| 240 | *argv = rawCharArrayList.data(); | 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 | return partialCopy(Context::scratchPath(), buffer, buffer_length); | 245 | return partialCopy(Context::scratchPath(), buffer, buffer_length); |
| 246 | } | 246 | } |
| @@ -298,9 +298,9 @@ const char *br_version() | @@ -298,9 +298,9 @@ const char *br_version() | ||
| 298 | return version.data(); | 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 | worker->transform = Globals->algorithm; | 304 | worker->transform = Globals->algorithm; |
| 305 | worker->baseName = baseName; | 305 | worker->baseName = baseName; |
| 306 | worker->mainLoop(); | 306 | worker->mainLoop(); |
| @@ -371,7 +371,7 @@ bool br_img_is_empty(br_template tmpl) | @@ -371,7 +371,7 @@ bool br_img_is_empty(br_template tmpl) | ||
| 371 | return t->m().empty(); | 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 | return partialCopy(reinterpret_cast<Template*>(tmpl)->file.name, buffer, buffer_length); | 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,7 +382,7 @@ void br_set_filename(br_template tmpl, const char *filename) | ||
| 382 | t->file.name = filename; | 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 | Template *t = reinterpret_cast<Template*>(tmpl); | 387 | Template *t = reinterpret_cast<Template*>(tmpl); |
| 388 | QVariant qvar = t->file.value(key); | 388 | QVariant qvar = t->file.value(key); |
openbr/openbr_plugin.cpp
| @@ -585,16 +585,51 @@ QStringList Object::parameters() const | @@ -585,16 +585,51 @@ QStringList Object::parameters() const | ||
| 585 | return parameters; | 585 | return parameters; |
| 586 | } | 586 | } |
| 587 | 587 | ||
| 588 | -QStringList Object::arguments() const | 588 | +QStringList Object::prunedArguments(bool expanded) const |
| 589 | { | 589 | { |
| 590 | QStringList arguments; | 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 | return arguments; | 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 | if ((index < 0) || (index > metaObject()->propertyCount())) return ""; | 634 | if ((index < 0) || (index > metaObject()->propertyCount())) return ""; |
| 600 | const QMetaProperty property = metaObject()->property(index); | 635 | const QMetaProperty property = metaObject()->property(index); |
| @@ -612,19 +647,19 @@ QString Object::argument(int index) const | @@ -612,19 +647,19 @@ QString Object::argument(int index) const | ||
| 612 | strings.append(QString::number(value)); | 647 | strings.append(QString::number(value)); |
| 613 | } else if (type == "QList<br::Transform*>") { | 648 | } else if (type == "QList<br::Transform*>") { |
| 614 | foreach (Transform *transform, variant.value< QList<Transform*> >()) | 649 | foreach (Transform *transform, variant.value< QList<Transform*> >()) |
| 615 | - strings.append(transform->description()); | 650 | + strings.append(transform->description(expanded)); |
| 616 | } else if (type == "QList<br::Distance*>") { | 651 | } else if (type == "QList<br::Distance*>") { |
| 617 | foreach (Distance *distance, variant.value< QList<Distance*> >()) | 652 | foreach (Distance *distance, variant.value< QList<Distance*> >()) |
| 618 | - strings.append(distance->description()); | 653 | + strings.append(distance->description(expanded)); |
| 619 | } else { | 654 | } else { |
| 620 | qFatal("Unrecognized type: %s", qPrintable(type)); | 655 | qFatal("Unrecognized type: %s", qPrintable(type)); |
| 621 | } | 656 | } |
| 622 | 657 | ||
| 623 | return "[" + strings.join(",") + "]"; | 658 | return "[" + strings.join(",") + "]"; |
| 624 | } else if (type == "br::Transform*") { | 659 | } else if (type == "br::Transform*") { |
| 625 | - return variant.value<Transform*>()->description(); | 660 | + return variant.value<Transform*>()->description(expanded); |
| 626 | } else if (type == "br::Distance*") { | 661 | } else if (type == "br::Distance*") { |
| 627 | - return variant.value<Distance*>()->description(); | 662 | + return variant.value<Distance*>()->description(expanded); |
| 628 | } else if (type == "QStringList") { | 663 | } else if (type == "QStringList") { |
| 629 | return "[" + variant.toStringList().join(",") + "]"; | 664 | return "[" + variant.toStringList().join(",") + "]"; |
| 630 | } | 665 | } |
| @@ -632,9 +667,12 @@ QString Object::argument(int index) const | @@ -632,9 +667,12 @@ QString Object::argument(int index) const | ||
| 632 | return variant.toString(); | 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 | return objectName() + (argumentString.isEmpty() ? "" : ("(" + argumentString + ")")); | 676 | return objectName() + (argumentString.isEmpty() ? "" : ("(" + argumentString + ")")); |
| 639 | } | 677 | } |
| 640 | 678 | ||
| @@ -1292,7 +1330,7 @@ Transform *Transform::make(QString str, QObject *parent) | @@ -1292,7 +1330,7 @@ Transform *Transform::make(QString str, QObject *parent) | ||
| 1292 | 1330 | ||
| 1293 | Transform *Transform::clone() const | 1331 | Transform *Transform::clone() const |
| 1294 | { | 1332 | { |
| 1295 | - Transform *clone = Factory<Transform>::make(file.flat()); | 1333 | + Transform *clone = Factory<Transform>::make("."+description(false)); |
| 1296 | return clone; | 1334 | return clone; |
| 1297 | } | 1335 | } |
| 1298 | 1336 | ||
| @@ -1461,3 +1499,17 @@ void br::applyAdditionalProperties(const File &temp, Transform *target) | @@ -1461,3 +1499,17 @@ void br::applyAdditionalProperties(const File &temp, Transform *target) | ||
| 1461 | target->setPropertyRecursive(i.key(), i.value() ); | 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,10 +593,18 @@ public: | ||
| 593 | virtual void store(QDataStream &stream) const; /*!< \brief Serialize the object. */ | 593 | virtual void store(QDataStream &stream) const; /*!< \brief Serialize the object. */ |
| 594 | virtual void load(QDataStream &stream); /*!< \brief Deserialize the object. Default implementation calls init() after deserialization. */ | 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 | QStringList parameters() const; /*!< \brief A string describing the parameters the object takes. */ | 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 | void setProperty(const QString &name, QVariant value); /*!< \brief Overload of QObject::setProperty to handle OpenBR data types. */ | 608 | void setProperty(const QString &name, QVariant value); /*!< \brief Overload of QObject::setProperty to handle OpenBR data types. */ |
| 601 | 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. */ | 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,6 +1282,21 @@ public: | ||
| 1274 | */ | 1282 | */ |
| 1275 | QList<Transform *> getChildren() const; | 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 | protected: | 1300 | protected: |
| 1278 | Transform(bool independent = true, bool trainable = true); /*!< \brief Construct a transform. */ | 1301 | Transform(bool independent = true, bool trainable = true); /*!< \brief Construct a transform. */ |
| 1279 | inline Transform *make(const QString &description) { return make(description, this); } /*!< \brief Make a subtransform. */ | 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,6 +251,7 @@ private: | ||
| 251 | default: | 251 | default: |
| 252 | qFatal("Invalid operation."); | 252 | qFatal("Invalid operation."); |
| 253 | } | 253 | } |
| 254 | + return 0; | ||
| 254 | } | 255 | } |
| 255 | 256 | ||
| 256 | void store(QDataStream &stream) const | 257 | void store(QDataStream &stream) const |
| @@ -460,13 +461,12 @@ BR_REGISTER(Distance, SumDistance) | @@ -460,13 +461,12 @@ BR_REGISTER(Distance, SumDistance) | ||
| 460 | class GalleryCompareTransform : public Transform | 461 | class GalleryCompareTransform : public Transform |
| 461 | { | 462 | { |
| 462 | Q_OBJECT | 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 | Q_PROPERTY(QString galleryName READ get_galleryName WRITE set_galleryName RESET reset_galleryName STORED false) | 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 | BR_PROPERTY(QString, galleryName, "") | 467 | BR_PROPERTY(QString, galleryName, "") |
| 467 | 468 | ||
| 468 | TemplateList gallery; | 469 | TemplateList gallery; |
| 469 | - QSharedPointer<Distance> distance; | ||
| 470 | 470 | ||
| 471 | void project(const Template &src, Template &dst) const | 471 | void project(const Template &src, Template &dst) const |
| 472 | { | 472 | { |
| @@ -480,16 +480,29 @@ class GalleryCompareTransform : public Transform | @@ -480,16 +480,29 @@ class GalleryCompareTransform : public Transform | ||
| 480 | 480 | ||
| 481 | void init() | 481 | void init() |
| 482 | { | 482 | { |
| 483 | - if (!galleryName.isEmpty()) { | 483 | + if (!galleryName.isEmpty()) |
| 484 | gallery = TemplateList::fromGallery(galleryName); | 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 | public: | 504 | public: |
| 492 | - GalleryCompareTransform() : Transform(false, false) {} | 505 | + GalleryCompareTransform() : Transform(false, true) {} |
| 493 | }; | 506 | }; |
| 494 | 507 | ||
| 495 | BR_REGISTER(Transform, GalleryCompareTransform) | 508 | BR_REGISTER(Transform, GalleryCompareTransform) |
openbr/plugins/frames.cpp
openbr/plugins/independent.cpp
| @@ -94,6 +94,12 @@ class DownsampleTrainingTransform : public Transform | @@ -94,6 +94,12 @@ class DownsampleTrainingTransform : public Transform | ||
| 94 | BR_PROPERTY(QStringList, subjects, QStringList()) | 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 | void project(const Template &src, Template &dst) const | 103 | void project(const Template &src, Template &dst) const |
| 98 | { | 104 | { |
| 99 | transform->project(src,dst); | 105 | transform->project(src,dst); |
| @@ -126,6 +132,10 @@ class IndependentTransform : public MetaTransform | @@ -126,6 +132,10 @@ class IndependentTransform : public MetaTransform | ||
| 126 | 132 | ||
| 127 | QList<Transform*> transforms; | 133 | QList<Transform*> transforms; |
| 128 | 134 | ||
| 135 | + QString description(bool expanded) const | ||
| 136 | + { | ||
| 137 | + return transform->description(expanded); | ||
| 138 | + } | ||
| 129 | 139 | ||
| 130 | bool setPropertyRecursive(const QString &name, QVariant value) | 140 | bool setPropertyRecursive(const QString &name, QVariant value) |
| 131 | { | 141 | { |
| @@ -141,6 +151,54 @@ class IndependentTransform : public MetaTransform | @@ -141,6 +151,54 @@ class IndependentTransform : public MetaTransform | ||
| 141 | return true; | 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 | void init() | 202 | void init() |
| 145 | { | 203 | { |
| 146 | transforms.clear(); | 204 | transforms.clear(); |
openbr/plugins/meta.cpp
| @@ -482,17 +482,32 @@ BR_REGISTER(Transform, CacheTransform) | @@ -482,17 +482,32 @@ BR_REGISTER(Transform, CacheTransform) | ||
| 482 | class LoadStoreTransform : public MetaTransform | 482 | class LoadStoreTransform : public MetaTransform |
| 483 | { | 483 | { |
| 484 | Q_OBJECT | 484 | Q_OBJECT |
| 485 | - Q_PROPERTY(QString description READ get_description WRITE set_description RESET reset_description STORED false) | 485 | + Q_PROPERTY(QString transformString READ get_transformString WRITE set_transformString RESET reset_transformString STORED false) |
| 486 | Q_PROPERTY(QString fileName READ get_fileName WRITE set_fileName RESET reset_fileName STORED false) | 486 | Q_PROPERTY(QString fileName READ get_fileName WRITE set_fileName RESET reset_fileName STORED false) |
| 487 | - BR_PROPERTY(QString, description, "Identity") | 487 | + BR_PROPERTY(QString, transformString, "Identity") |
| 488 | BR_PROPERTY(QString, fileName, QString()) | 488 | BR_PROPERTY(QString, fileName, QString()) |
| 489 | 489 | ||
| 490 | +public: | ||
| 490 | Transform *transform; | 491 | Transform *transform; |
| 491 | QString baseName; | 492 | QString baseName; |
| 492 | 493 | ||
| 493 | -public: | ||
| 494 | LoadStoreTransform() : transform(NULL) {} | 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 | bool setPropertyRecursive(const QString &name, QVariant value) | 511 | bool setPropertyRecursive(const QString &name, QVariant value) |
| 497 | { | 512 | { |
| 498 | if (br::Object::setPropertyRecursive(name, value)) | 513 | if (br::Object::setPropertyRecursive(name, value)) |
| @@ -500,13 +515,16 @@ public: | @@ -500,13 +515,16 @@ public: | ||
| 500 | return transform->setPropertyRecursive(name, value); | 515 | return transform->setPropertyRecursive(name, value); |
| 501 | } | 516 | } |
| 502 | private: | 517 | private: |
| 518 | + | ||
| 503 | void init() | 519 | void init() |
| 504 | { | 520 | { |
| 505 | if (transform != NULL) return; | 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 | else baseName = fileName; | 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 | bool timeVarying() const | 530 | bool timeVarying() const |
| @@ -524,7 +542,7 @@ private: | @@ -524,7 +542,7 @@ private: | ||
| 524 | qDebug("Storing %s", qPrintable(baseName)); | 542 | qDebug("Storing %s", qPrintable(baseName)); |
| 525 | QByteArray byteArray; | 543 | QByteArray byteArray; |
| 526 | QDataStream stream(&byteArray, QFile::WriteOnly); | 544 | QDataStream stream(&byteArray, QFile::WriteOnly); |
| 527 | - stream << description; | 545 | + stream << transform->description(); |
| 528 | transform->store(stream); | 546 | transform->store(stream); |
| 529 | QtUtils::writeFile(baseName, byteArray, -1); | 547 | QtUtils::writeFile(baseName, byteArray, -1); |
| 530 | } | 548 | } |
| @@ -570,8 +588,8 @@ private: | @@ -570,8 +588,8 @@ private: | ||
| 570 | QByteArray data; | 588 | QByteArray data; |
| 571 | QtUtils::readFile(file, data, true); | 589 | QtUtils::readFile(file, data, true); |
| 572 | QDataStream stream(&data, QFile::ReadOnly); | 590 | QDataStream stream(&data, QFile::ReadOnly); |
| 573 | - stream >> description; | ||
| 574 | - transform = Transform::make(description); | 591 | + stream >> transformString; |
| 592 | + transform = Transform::make(transformString); | ||
| 575 | transform->load(stream); | 593 | transform->load(stream); |
| 576 | return true; | 594 | return true; |
| 577 | } | 595 | } |
openbr/plugins/misc.cpp
| @@ -617,8 +617,8 @@ class ProgressCounterTransform : public TimeVaryingTransform | @@ -617,8 +617,8 @@ class ProgressCounterTransform : public TimeVaryingTransform | ||
| 617 | { | 617 | { |
| 618 | Q_OBJECT | 618 | Q_OBJECT |
| 619 | 619 | ||
| 620 | - Q_PROPERTY(int totalTemplates READ get_totalTemplates WRITE set_totalTemplates RESET reset_totalTemplates STORED false) | ||
| 621 | - BR_PROPERTY(int, totalTemplates, 1) | 620 | + Q_PROPERTY(int totalProgress READ get_totalProgress WRITE set_totalProgress RESET reset_totalProgress STORED false) |
| 621 | + BR_PROPERTY(int, totalProgress, 1) | ||
| 622 | 622 | ||
| 623 | void projectUpdate(const TemplateList &src, TemplateList &dst) | 623 | void projectUpdate(const TemplateList &src, TemplateList &dst) |
| 624 | { | 624 | { |
| @@ -658,16 +658,20 @@ class ProgressCounterTransform : public TimeVaryingTransform | @@ -658,16 +658,20 @@ class ProgressCounterTransform : public TimeVaryingTransform | ||
| 658 | (void) data; | 658 | (void) data; |
| 659 | float p = br_progress(); | 659 | float p = br_progress(); |
| 660 | qDebug("\r%05.2f%% ELAPSED=%s REMAINING=%s COUNT=%g", p*100, QtUtils::toTime(Globals->startTime.elapsed()/1000.0f).toStdString().c_str(), QtUtils::toTime(0).toStdString().c_str(), Globals->currentStep); | 660 | qDebug("\r%05.2f%% ELAPSED=%s REMAINING=%s COUNT=%g", p*100, QtUtils::toTime(Globals->startTime.elapsed()/1000.0f).toStdString().c_str(), QtUtils::toTime(0).toStdString().c_str(), Globals->currentStep); |
| 661 | + timer.start(); | ||
| 662 | + Globals->startTime.start(); | ||
| 661 | Globals->currentStep = 0; | 663 | Globals->currentStep = 0; |
| 662 | Globals->currentProgress = 0; | 664 | Globals->currentProgress = 0; |
| 663 | - Globals->totalSteps = 0; | 665 | + Globals->totalSteps = totalProgress; |
| 664 | } | 666 | } |
| 665 | 667 | ||
| 666 | void init() | 668 | void init() |
| 667 | { | 669 | { |
| 668 | timer.start(); | 670 | timer.start(); |
| 671 | + Globals->startTime.start(); | ||
| 669 | Globals->currentStep = 0; | 672 | Globals->currentStep = 0; |
| 670 | Globals->currentProgress = 0; | 673 | Globals->currentProgress = 0; |
| 674 | + Globals->totalSteps = totalProgress; | ||
| 671 | } | 675 | } |
| 672 | 676 | ||
| 673 | public: | 677 | public: |
openbr/plugins/openbr_internal.h
| @@ -201,6 +201,33 @@ public: | @@ -201,6 +201,33 @@ public: | ||
| 201 | this->trainable = transform->trainable; | 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 | bool setPropertyRecursive(const QString &name, QVariant value) | 231 | bool setPropertyRecursive(const QString &name, QVariant value) |
| 205 | { | 232 | { |
| 206 | if (br::Object::setPropertyRecursive(name, value)) | 233 | if (br::Object::setPropertyRecursive(name, value)) |
| @@ -212,6 +239,34 @@ public: | @@ -212,6 +239,34 @@ public: | ||
| 212 | } | 239 | } |
| 213 | return false; | 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,21 +325,10 @@ public: | ||
| 270 | } | 325 | } |
| 271 | newTransform = true; | 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 | if (output == NULL) | 333 | if (output == NULL) |
| 290 | qFatal("Dynamic cast failed!"); | 334 | qFatal("Dynamic cast failed!"); |
| @@ -303,6 +347,51 @@ public: | @@ -303,6 +347,51 @@ public: | ||
| 303 | return output; | 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 | bool setPropertyRecursive(const QString &name, QVariant value) | 395 | bool setPropertyRecursive(const QString &name, QVariant value) |
| 307 | { | 396 | { |
| 308 | if (br::Object::setPropertyRecursive(name, value)) | 397 | if (br::Object::setPropertyRecursive(name, value)) |
| @@ -388,6 +477,10 @@ public: | @@ -388,6 +477,10 @@ public: | ||
| 388 | 477 | ||
| 389 | void applyAdditionalProperties(const File &temp, Transform *target); | 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 | #endif // OPENBR_INTERNAL_H | 486 | #endif // OPENBR_INTERNAL_H |
openbr/plugins/pp5.cpp
| @@ -450,8 +450,9 @@ class PP5GalleryTransform: public UntrainableMetaTransform | @@ -450,8 +450,9 @@ class PP5GalleryTransform: public UntrainableMetaTransform | ||
| 450 | 450 | ||
| 451 | ppr_gallery_type target; | 451 | ppr_gallery_type target; |
| 452 | QList<int> targetIDs; | 452 | QList<int> targetIDs; |
| 453 | + TemplateList gallery; | ||
| 453 | 454 | ||
| 454 | - void project(const Template & src, Template & dst) const | 455 | + void project(const Template &src, Template &dst) const |
| 455 | { | 456 | { |
| 456 | TemplateList temp, output; | 457 | TemplateList temp, output; |
| 457 | temp.append(src); | 458 | temp.append(src); |
| @@ -460,7 +461,7 @@ class PP5GalleryTransform: public UntrainableMetaTransform | @@ -460,7 +461,7 @@ class PP5GalleryTransform: public UntrainableMetaTransform | ||
| 460 | dst = output[0]; | 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 | dst.clear(); | 466 | dst.clear(); |
| 466 | QList<int> queryIDs; | 467 | QList<int> queryIDs; |
| @@ -495,11 +496,33 @@ class PP5GalleryTransform: public UntrainableMetaTransform | @@ -495,11 +496,33 @@ class PP5GalleryTransform: public UntrainableMetaTransform | ||
| 495 | 496 | ||
| 496 | void init() | 497 | void init() |
| 497 | { | 498 | { |
| 498 | - // set up the gallery | ||
| 499 | - ppr_create_gallery(context, &target); | ||
| 500 | - TemplateList templates = TemplateList::fromGallery(galleryName); | ||
| 501 | - enroll(templates,&target, targetIDs); | 499 | + if (!galleryName.isEmpty() || !gallery.isEmpty()) { |
| 500 | + // set up the gallery | ||
| 501 | + ppr_create_gallery(context, &target); | ||
| 502 | + if (gallery.isEmpty() ) | ||
| 503 | + gallery = TemplateList::fromGallery(galleryName); | ||
| 504 | + enroll(gallery, &target, targetIDs); | ||
| 505 | + } | ||
| 506 | + } | ||
| 507 | + | ||
| 508 | + void train(const TemplateList &data) | ||
| 509 | + { | ||
| 510 | + gallery = data; | ||
| 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 | BR_REGISTER(Transform, PP5GalleryTransform) | 528 | BR_REGISTER(Transform, PP5GalleryTransform) |
openbr/plugins/process.cpp
| @@ -21,7 +21,7 @@ class CommunicationManager : public QObject | @@ -21,7 +21,7 @@ class CommunicationManager : public QObject | ||
| 21 | { | 21 | { |
| 22 | Q_OBJECT | 22 | Q_OBJECT |
| 23 | public: | 23 | public: |
| 24 | - QThread * basis; | 24 | + QThread *basis; |
| 25 | CommunicationManager() | 25 | CommunicationManager() |
| 26 | { | 26 | { |
| 27 | basis = new QThread; | 27 | basis = new QThread; |
| @@ -294,7 +294,7 @@ public: | @@ -294,7 +294,7 @@ public: | ||
| 294 | QString serverName; | 294 | QString serverName; |
| 295 | QString remoteName; | 295 | QString remoteName; |
| 296 | 296 | ||
| 297 | - QLocalSocket * inbound; | 297 | + QLocalSocket *inbound; |
| 298 | QLocalSocket outbound; | 298 | QLocalSocket outbound; |
| 299 | QLocalServer server; | 299 | QLocalServer server; |
| 300 | 300 | ||
| @@ -305,13 +305,11 @@ public: | @@ -305,13 +305,11 @@ public: | ||
| 305 | while (!inbound || inbound->state() != QLocalSocket::ConnectedState) { | 305 | while (!inbound || inbound->state() != QLocalSocket::ConnectedState) { |
| 306 | bool res = receivedWait.wait(&receivedLock,30*1000); | 306 | bool res = receivedWait.wait(&receivedLock,30*1000); |
| 307 | if (!res) | 307 | if (!res) |
| 308 | - { | ||
| 309 | qDebug() << key << " " << QThread::currentThread() << " waiting timed out, server thread is " << server.thread() << " base thread " << basis; | 308 | qDebug() << key << " " << QThread::currentThread() << " waiting timed out, server thread is " << server.thread() << " base thread " << basis; |
| 310 | - } | ||
| 311 | } | 309 | } |
| 312 | } | 310 | } |
| 313 | 311 | ||
| 314 | - void connectToRemote(const QString & remoteName) | 312 | + void connectToRemote(const QString &remoteName) |
| 315 | { | 313 | { |
| 316 | emit pulseOutboundConnect(remoteName); | 314 | emit pulseOutboundConnect(remoteName); |
| 317 | 315 | ||
| @@ -330,7 +328,7 @@ public: | @@ -330,7 +328,7 @@ public: | ||
| 330 | 328 | ||
| 331 | 329 | ||
| 332 | template<typename T> | 330 | template<typename T> |
| 333 | - bool readData(T & input) | 331 | + bool readData(T &input) |
| 334 | { | 332 | { |
| 335 | emit pulseReadSerialized(); | 333 | emit pulseReadSerialized(); |
| 336 | QDataStream deserializer(readArray); | 334 | QDataStream deserializer(readArray); |
| @@ -338,8 +336,18 @@ public: | @@ -338,8 +336,18 @@ public: | ||
| 338 | return true; | 336 | return true; |
| 339 | } | 337 | } |
| 340 | 338 | ||
| 339 | + Transform *readTForm() | ||
| 340 | + { | ||
| 341 | + emit pulseReadSerialized(); | ||
| 342 | + | ||
| 343 | + QByteArray data = readArray; | ||
| 344 | + QDataStream deserializer(data); | ||
| 345 | + Transform *res = Transform::deserialize(deserializer); | ||
| 346 | + return res; | ||
| 347 | + } | ||
| 348 | + | ||
| 341 | template<typename T> | 349 | template<typename T> |
| 342 | - bool sendData(const T & output) | 350 | + bool sendData(const T &output) |
| 343 | { | 351 | { |
| 344 | QBuffer buffer; | 352 | QBuffer buffer; |
| 345 | buffer.open(QBuffer::ReadWrite); | 353 | buffer.open(QBuffer::ReadWrite); |
| @@ -378,7 +386,7 @@ class EnrollmentWorker : public QObject | @@ -378,7 +386,7 @@ class EnrollmentWorker : public QObject | ||
| 378 | { | 386 | { |
| 379 | Q_OBJECT | 387 | Q_OBJECT |
| 380 | public: | 388 | public: |
| 381 | - CommunicationManager * comm; | 389 | + CommunicationManager *comm; |
| 382 | QString name; | 390 | QString name; |
| 383 | 391 | ||
| 384 | ~EnrollmentWorker() | 392 | ~EnrollmentWorker() |
| @@ -389,10 +397,10 @@ public: | @@ -389,10 +397,10 @@ public: | ||
| 389 | delete comm; | 397 | delete comm; |
| 390 | } | 398 | } |
| 391 | 399 | ||
| 392 | - br::Transform * transform; | 400 | + br::Transform *transform; |
| 393 | 401 | ||
| 394 | public: | 402 | public: |
| 395 | - void connections(const QString & baseName) | 403 | + void connections(const QString &baseName) |
| 396 | { | 404 | { |
| 397 | comm = new CommunicationManager(); | 405 | comm = new CommunicationManager(); |
| 398 | name = baseName; | 406 | name = baseName; |
| @@ -401,6 +409,8 @@ public: | @@ -401,6 +409,8 @@ public: | ||
| 401 | comm->connectToRemote(baseName+"_master"); | 409 | comm->connectToRemote(baseName+"_master"); |
| 402 | 410 | ||
| 403 | comm->waitForInbound(); | 411 | comm->waitForInbound(); |
| 412 | + | ||
| 413 | + transform = comm->readTForm(); | ||
| 404 | } | 414 | } |
| 405 | 415 | ||
| 406 | void workerLoop() | 416 | void workerLoop() |
| @@ -428,7 +438,7 @@ public: | @@ -428,7 +438,7 @@ public: | ||
| 428 | void WorkerProcess::mainLoop() | 438 | void WorkerProcess::mainLoop() |
| 429 | { | 439 | { |
| 430 | processInterface = new EnrollmentWorker(); | 440 | processInterface = new EnrollmentWorker(); |
| 431 | - processInterface->transform = Transform::make(this->transform,NULL); | 441 | + processInterface->transform = NULL; |
| 432 | processInterface->connections(baseName); | 442 | processInterface->connections(baseName); |
| 433 | processInterface->workerLoop(); | 443 | processInterface->workerLoop(); |
| 434 | delete processInterface; | 444 | delete processInterface; |
| @@ -478,63 +488,99 @@ protected slots: | @@ -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 | * \ingroup transforms | 512 | * \ingroup transforms |
| 483 | * \brief Interface to a separate process | 513 | * \brief Interface to a separate process |
| 484 | * \author Charles Otto \cite caotto | 514 | * \author Charles Otto \cite caotto |
| 485 | */ | 515 | */ |
| 486 | -class ProcessWrapperTransform : public TimeVaryingTransform | 516 | +class ProcessWrapperTransform : public WrapperTransform |
| 487 | { | 517 | { |
| 488 | Q_OBJECT | 518 | Q_OBJECT |
| 489 | 519 | ||
| 490 | - Q_PROPERTY(QString transform READ get_transform WRITE set_transform RESET reset_transform) | ||
| 491 | - BR_PROPERTY(QString, transform, "") | ||
| 492 | - | ||
| 493 | QString baseKey; | 520 | QString baseKey; |
| 494 | 521 | ||
| 495 | - ProcessInterface workerProcess; | ||
| 496 | - CommunicationManager * comm; | 522 | + Resource<ProcessData> processes; |
| 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 | if (src.empty()) | 532 | if (src.empty()) |
| 501 | return; | 533 | return; |
| 534 | + | ||
| 535 | + ProcessData *data = processes.acquire(); | ||
| 536 | + if (!data->initialized) | ||
| 537 | + activateProcess(data); | ||
| 502 | 538 | ||
| 503 | - if (!processActive) | ||
| 504 | - { | ||
| 505 | - activateProcess(); | ||
| 506 | - } | ||
| 507 | - comm->sendSignal(CommunicationManager::INPUT_AVAILABLE); | ||
| 508 | - | ||
| 509 | - comm->sendData(src); | 539 | + CommunicationManager *localComm = &(data->comm); |
| 540 | + localComm->sendSignal(CommunicationManager::INPUT_AVAILABLE); | ||
| 510 | 541 | ||
| 511 | - comm->readData(dst); | 542 | + localComm->sendData(src); |
| 543 | + localComm->readData(dst); | ||
| 544 | + processes.release(data); | ||
| 512 | } | 545 | } |
| 513 | 546 | ||
| 514 | - | ||
| 515 | void train(const TemplateList& data) | 547 | void train(const TemplateList& data) |
| 516 | { | 548 | { |
| 517 | (void) data; | 549 | (void) data; |
| 518 | } | 550 | } |
| 519 | 551 | ||
| 520 | - // create the process | ||
| 521 | void init() | 552 | void init() |
| 522 | { | 553 | { |
| 523 | processActive = false; | 554 | processActive = false; |
| 555 | + serialized.clear(); | ||
| 556 | + if (transform) { | ||
| 557 | + QDataStream out(&serialized, QFile::WriteOnly); | ||
| 558 | + transform->serialize(out); | ||
| 559 | + } | ||
| 524 | } | 560 | } |
| 525 | 561 | ||
| 526 | - void activateProcess() | 562 | + QByteArray serialized; |
| 563 | + void transmitTForm(CommunicationManager *localComm) const | ||
| 527 | { | 564 | { |
| 528 | - comm = new CommunicationManager(); | ||
| 529 | - processActive = true; | 565 | + if (serialized.isEmpty() ) |
| 566 | + qFatal("Trying to transmit empty transform!"); | ||
| 567 | + | ||
| 568 | + localComm->writeArray = serialized; | ||
| 569 | + emit localComm->pulseSendSerialized(); | ||
| 570 | + } | ||
| 530 | 571 | ||
| 572 | + void activateProcess(ProcessData *data) const | ||
| 573 | + { | ||
| 574 | + data->initialized = true; | ||
| 531 | // generate a uuid for our local servers | 575 | // generate a uuid for our local servers |
| 532 | QUuid id = QUuid::createUuid(); | 576 | QUuid id = QUuid::createUuid(); |
| 533 | - baseKey = id.toString(); | 577 | + QString baseKey = id.toString(); |
| 534 | 578 | ||
| 535 | QStringList argumentList; | 579 | QStringList argumentList; |
| 580 | + // We serialize and transmit the transform directly, so algorithm doesn't matter. | ||
| 581 | + argumentList.append("-quiet"); | ||
| 536 | argumentList.append("-algorithm"); | 582 | argumentList.append("-algorithm"); |
| 537 | - argumentList.append(transform); | 583 | + argumentList.append("Identity"); |
| 538 | if (!Globals->path.isEmpty()) { | 584 | if (!Globals->path.isEmpty()) { |
| 539 | argumentList.append("-path"); | 585 | argumentList.append("-path"); |
| 540 | argumentList.append(Globals->path); | 586 | argumentList.append(Globals->path); |
| @@ -544,36 +590,25 @@ class ProcessWrapperTransform : public TimeVaryingTransform | @@ -544,36 +590,25 @@ class ProcessWrapperTransform : public TimeVaryingTransform | ||
| 544 | argumentList.append("-slave"); | 590 | argumentList.append("-slave"); |
| 545 | argumentList.append(baseKey); | 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 | public: | 609 | public: |
| 575 | bool processActive; | 610 | bool processActive; |
| 576 | - ProcessWrapperTransform() : TimeVaryingTransform(false,false) { processActive = false; } | 611 | + ProcessWrapperTransform() : WrapperTransform(false) { processActive = false; } |
| 577 | }; | 612 | }; |
| 578 | 613 | ||
| 579 | BR_REGISTER(Transform, ProcessWrapperTransform) | 614 | BR_REGISTER(Transform, ProcessWrapperTransform) |
openbr/plugins/validate.cpp
| @@ -7,7 +7,7 @@ | @@ -7,7 +7,7 @@ | ||
| 7 | namespace br | 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 | transform->train(data); | 12 | transform->train(data); |
| 13 | } | 13 | } |