Commit 61a43e72af505bb90e215f00a1a8fd2968ff95b9
Merge branch 'master' of https://github.com/biometrics/openbr
Showing
8 changed files
with
444 additions
and
79 deletions
openbr/core/core.cpp
| ... | ... | @@ -29,6 +29,9 @@ struct AlgorithmCore |
| 29 | 29 | QSharedPointer<Transform> transform; |
| 30 | 30 | QSharedPointer<Distance> distance; |
| 31 | 31 | |
| 32 | + QString transformString; | |
| 33 | + QString distanceString; | |
| 34 | + | |
| 32 | 35 | AlgorithmCore(const QString &name) |
| 33 | 36 | { |
| 34 | 37 | this->name = name; |
| ... | ... | @@ -134,6 +137,8 @@ struct AlgorithmCore |
| 134 | 137 | } |
| 135 | 138 | TemplateList data(TemplateList::fromGallery(input)); |
| 136 | 139 | |
| 140 | + bool multiProcess = Globals->file.getBool("multiProcess", false); | |
| 141 | + | |
| 137 | 142 | if (gallery.contains("append")) |
| 138 | 143 | { |
| 139 | 144 | // Remove any templates which are already in the gallery |
| ... | ... | @@ -155,20 +160,27 @@ struct AlgorithmCore |
| 155 | 160 | Globals->currentStep = 0; |
| 156 | 161 | Globals->totalSteps = data.length(); |
| 157 | 162 | |
| 158 | - // Trust me, this makes complete sense. | |
| 159 | - // We're just going to make a pipe with a placeholder first transform | |
| 160 | - QString pipeDesc = "Identity+GalleryOutput("+gallery.flat()+")+ProgressCounter("+QString::number(data.length())+")+Discard"; | |
| 161 | - QScopedPointer<Transform> basePipe(Transform::make(pipeDesc,NULL)); | |
| 163 | + QScopedPointer<Transform> basePipe; | |
| 162 | 164 | |
| 163 | - CompositeTransform * downcast = dynamic_cast<CompositeTransform *>(basePipe.data()); | |
| 164 | - if (downcast == NULL) | |
| 165 | - qFatal("downcast failed?"); | |
| 165 | + if (!multiProcess) | |
| 166 | + { | |
| 167 | + QString pipeDesc = "Identity+GalleryOutput("+gallery.flat()+")+ProgressCounter("+QString::number(data.length())+")+Discard"; | |
| 168 | + basePipe.reset(Transform::make(pipeDesc,NULL)); | |
| 169 | + CompositeTransform * downcast = dynamic_cast<CompositeTransform *>(basePipe.data()); | |
| 170 | + if (downcast == NULL) | |
| 171 | + qFatal("downcast failed?"); | |
| 166 | 172 | |
| 167 | - // replace that placeholder with the current algorithm | |
| 168 | - downcast->transforms[0] = this->transform.data(); | |
| 173 | + // replace that placeholder with the current algorithm | |
| 174 | + downcast->transforms[0] = this->transform.data(); | |
| 169 | 175 | |
| 170 | - // call init on the pipe to collapse the algorithm (if its top level is a pipe) | |
| 171 | - downcast->init(); | |
| 176 | + // call init on the pipe to collapse the algorithm (if its top level is a pipe) | |
| 177 | + downcast->init(); | |
| 178 | + } | |
| 179 | + else | |
| 180 | + { | |
| 181 | + QString pipeDesc = "ProcessWrapper("+transformString+")"+"+GalleryOutput("+gallery.flat()+")+ProgressCounter("+QString::number(data.length())+")+Discard"; | |
| 182 | + basePipe.reset(Transform::make(pipeDesc,NULL)); | |
| 183 | + } | |
| 172 | 184 | |
| 173 | 185 | // Next, we make a Stream (with placeholder transform) |
| 174 | 186 | QString streamDesc = "Stream(Identity, readMode=DistributeFrames)"; |
| ... | ... | @@ -176,7 +188,7 @@ struct AlgorithmCore |
| 176 | 188 | WrapperTransform * wrapper = dynamic_cast<WrapperTransform *> (baseStream.data()); |
| 177 | 189 | |
| 178 | 190 | // replace that placeholder with the pipe we built |
| 179 | - wrapper->transform = downcast; | |
| 191 | + wrapper->transform = basePipe.data(); | |
| 180 | 192 | |
| 181 | 193 | // and get the final stream's stages by reinterpreting the pipe. Perfectly straightforward. |
| 182 | 194 | wrapper->init(); |
| ... | ... | @@ -196,6 +208,38 @@ struct AlgorithmCore |
| 196 | 208 | data >> *transform; |
| 197 | 209 | } |
| 198 | 210 | |
| 211 | + // Read metadata for all templates stored in the specified gallery, return the read | |
| 212 | + // TeamplateList. If the gallery contains matrices, they are dropped. | |
| 213 | + void emptyRead(const File & file, TemplateList & templates) | |
| 214 | + { | |
| 215 | + // Is this a gallery type containing matrices? | |
| 216 | + if ((QStringList() << "gal" << "mem" << "template").contains(file.suffix())) { | |
| 217 | + // Retrieve it block by block, dropping matrices from read templates. | |
| 218 | + QScopedPointer<Gallery> gallery(Gallery::make(file)); | |
| 219 | + gallery->set_readBlockSize(10); | |
| 220 | + bool done = false; | |
| 221 | + while (!done) | |
| 222 | + { | |
| 223 | + TemplateList tList = gallery->readBlock(&done); | |
| 224 | + for (int i=0; i < tList.size();i++) | |
| 225 | + { | |
| 226 | + tList[i].clear(); | |
| 227 | + templates.append(tList[i]); | |
| 228 | + } | |
| 229 | + } | |
| 230 | + } | |
| 231 | + else { | |
| 232 | + // The file may have already been enrolled to a memory gallery | |
| 233 | + emptyRead(getMemoryGallery(file), templates); | |
| 234 | + if (!templates.empty()) | |
| 235 | + return; | |
| 236 | + | |
| 237 | + // Nope, just retrieve the metadata | |
| 238 | + QScopedPointer<Gallery> gallery(Gallery::make(file)); | |
| 239 | + templates = gallery->read(); | |
| 240 | + } | |
| 241 | + } | |
| 242 | + | |
| 199 | 243 | void retrieveOrEnroll(const File &file, QScopedPointer<Gallery> &gallery, FileList &galleryFiles) |
| 200 | 244 | { |
| 201 | 245 | if (!file.getBool("enroll") && (QStringList() << "gal" << "mem" << "template").contains(file.suffix())) { |
| ... | ... | @@ -241,17 +285,14 @@ struct AlgorithmCore |
| 241 | 285 | dummyTarget.append(targets[0]); |
| 242 | 286 | QScopedPointer<Output> realOutput(Output::make(output, dummyTarget, queryFiles)); |
| 243 | 287 | |
| 244 | - // Some outputs assume Globals->blockSize is a real thing, of course we have no interest in it. | |
| 245 | - int old_block_size = Globals->blockSize; | |
| 246 | - Globals->blockSize = INT_MAX; | |
| 288 | + realOutput->set_blockRows(INT_MAX); | |
| 289 | + realOutput->set_blockCols(INT_MAX); | |
| 247 | 290 | realOutput->setBlock(0,0); |
| 248 | 291 | for (int i=0; i < queries.length(); i++) |
| 249 | 292 | { |
| 250 | 293 | float res = distance->compare(queries[i], targets[i]); |
| 251 | 294 | realOutput->setRelative(res, 0,i); |
| 252 | 295 | } |
| 253 | - | |
| 254 | - Globals->blockSize = old_block_size; | |
| 255 | 296 | } |
| 256 | 297 | |
| 257 | 298 | void deduplicate(const File &inputGallery, const File &outputGallery, const float threshold) |
| ... | ... | @@ -310,71 +351,154 @@ struct AlgorithmCore |
| 310 | 351 | qPrintable(queryGallery.flat()), |
| 311 | 352 | output.isNull() ? "" : qPrintable(" to " + output.flat())); |
| 312 | 353 | |
| 354 | + bool multiProcess = Globals->file.getBool("multiProcess", false); | |
| 355 | + | |
| 313 | 356 | if (output.exists() && output.get<bool>("cache", false)) return; |
| 314 | 357 | if (queryGallery == ".") queryGallery = targetGallery; |
| 315 | 358 | |
| 316 | - QScopedPointer<Gallery> t, q; | |
| 317 | - FileList targetFiles, queryFiles; | |
| 318 | - retrieveOrEnroll(targetGallery, t, targetFiles); | |
| 319 | - retrieveOrEnroll(queryGallery, q, queryFiles); | |
| 359 | + // Read metadata for the target and query sets, the resulting | |
| 360 | + // TemplateLists do not contain matrices | |
| 361 | + TemplateList targetMetadata; | |
| 362 | + TemplateList queryMetadata; | |
| 320 | 363 | |
| 321 | - QList<int> partitionSizes; | |
| 322 | - QList<File> outputFiles; | |
| 323 | - if (output.contains("split")) { | |
| 324 | - if (!output.fileName().contains("%1")) qFatal("Output file name missing split number place marker (%%1)"); | |
| 325 | - partitionSizes = output.getList<int>("split"); | |
| 326 | - for (int i=0; i<partitionSizes.size(); i++) { | |
| 327 | - File splitOutputFile = output.name.arg(i); | |
| 328 | - outputFiles.append(splitOutputFile); | |
| 329 | - } | |
| 330 | - } | |
| 331 | - else outputFiles.append(output); | |
| 364 | + emptyRead(targetGallery, targetMetadata); | |
| 365 | + emptyRead(queryGallery, queryMetadata); | |
| 332 | 366 | |
| 333 | - QList<Output*> outputs; | |
| 334 | - foreach (const File &outputFile, outputFiles) outputs.append(Output::make(outputFile, targetFiles, queryFiles)); | |
| 367 | + // Enroll the metadata we read to memory galleries | |
| 368 | + File targetMetaMem = targetGallery; | |
| 369 | + targetMetaMem.name = targetMetaMem.baseName() + "_meta.mem"; | |
| 370 | + File queryMetaMem = queryGallery; | |
| 371 | + queryMetaMem.name = queryMetaMem.baseName() + "_meta.mem"; | |
| 335 | 372 | |
| 336 | - if (distance.isNull()) qFatal("Null distance."); | |
| 337 | - Globals->currentStep = 0; | |
| 338 | - Globals->totalSteps = double(targetFiles.size()) * double(queryFiles.size()); | |
| 339 | - Globals->startTime.start(); | |
| 373 | + // Store the metadata in memory galleries. | |
| 374 | + QScopedPointer<Gallery> targetMeta(Gallery::make(targetMetaMem)); | |
| 375 | + QScopedPointer<Gallery> queryMeta(Gallery::make(queryMetaMem)); | |
| 340 | 376 | |
| 341 | - int queryBlock = -1; | |
| 342 | - bool queryDone = false; | |
| 343 | - while (!queryDone) { | |
| 344 | - queryBlock++; | |
| 345 | - TemplateList queries = q->readBlock(&queryDone); | |
| 377 | + targetMeta->writeBlock(targetMetadata); | |
| 378 | + queryMeta->writeBlock(queryMetadata); | |
| 346 | 379 | |
| 347 | - QList<TemplateList> queryPartitions; | |
| 348 | - if (!partitionSizes.empty()) queryPartitions = queries.partition(partitionSizes); | |
| 349 | - else queryPartitions.append(queries); | |
| 350 | 380 | |
| 351 | - for (int i=0; i<queryPartitions.size(); i++) { | |
| 352 | - int targetBlock = -1; | |
| 353 | - bool targetDone = false; | |
| 354 | - while (!targetDone) { | |
| 355 | - targetBlock++; | |
| 381 | + // 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) | |
| 382 | + bool transposeCompare = targetMetadata.size() > queryMetadata.size(); | |
| 356 | 383 | |
| 357 | - TemplateList targets = t->readBlock(&targetDone); | |
| 384 | + File rowGallery = queryGallery; | |
| 385 | + File colGallery = targetGallery; | |
| 386 | + int rowSize = queryMetadata.size(); | |
| 358 | 387 | |
| 359 | - QList<TemplateList> targetPartitions; | |
| 360 | - if (!partitionSizes.empty()) targetPartitions = targets.partition(partitionSizes); | |
| 361 | - else targetPartitions.append(targets); | |
| 388 | + if (transposeCompare) | |
| 389 | + { | |
| 390 | + rowGallery = targetGallery; | |
| 391 | + colGallery = queryGallery; | |
| 392 | + rowSize = targetMetadata.size(); | |
| 393 | + } | |
| 362 | 394 | |
| 363 | - outputs[i]->setBlock(queryBlock, targetBlock); | |
| 395 | + // Do we need to enroll the row set? If so we will do it inline with the comparisons | |
| 396 | + bool needEnrollRows = false; | |
| 397 | + if (!(QStringList() << "gal" << "mem" << "template").contains(rowGallery.suffix())) | |
| 398 | + { | |
| 399 | + needEnrollRows = true; | |
| 400 | + } | |
| 364 | 401 | |
| 365 | - distance->compare(targetPartitions[i], queryPartitions[i], outputs[i]); | |
| 402 | + // Do we need to enroll the column set? We want it to be in a memory gallery, unless we | |
| 403 | + // are in multi-process mode | |
| 404 | + File colEnrolledGallery = colGallery; | |
| 405 | + QString targetExtension = multiProcess ? "gal" : "mem"; | |
| 406 | + if (colGallery.suffix() != targetExtension) | |
| 407 | + { | |
| 408 | + if (multiProcess) { | |
| 409 | + colEnrolledGallery = colGallery.baseName() + colGallery.hash() + ".gal"; | |
| 410 | + } | |
| 411 | + else { | |
| 412 | + colEnrolledGallery = colGallery.baseName() + colGallery.hash() + ".mem"; | |
| 413 | + } | |
| 366 | 414 | |
| 367 | - Globals->currentStep += double(targets.size()) * double(queries.size()); | |
| 368 | - Globals->printStatus(); | |
| 369 | - } | |
| 415 | + // We have to do actual enrollment if the gallery just specified metadata | |
| 416 | + if (!(QStringList() << "gal" << "template" << "mem").contains(colGallery.suffix())) | |
| 417 | + { | |
| 418 | + enroll(colGallery, colEnrolledGallery); | |
| 419 | + } | |
| 420 | + // If it did specify templates, but wasn't the write type, we still need to convert | |
| 421 | + // to the correct gallery type. | |
| 422 | + else | |
| 423 | + { | |
| 424 | + QScopedPointer<Gallery> readColGallery(Gallery::make(colGallery)); | |
| 425 | + TemplateList templates = readColGallery->read(); | |
| 426 | + QScopedPointer<Gallery> enrolledColOutput(Gallery::make(colEnrolledGallery)); | |
| 427 | + enrolledColOutput->writeBlock(templates); | |
| 370 | 428 | } |
| 371 | 429 | } |
| 372 | 430 | |
| 373 | - qDeleteAll(outputs); | |
| 431 | + // Describe a GalleryCompare transform, using the data we enrolled | |
| 432 | + QString compareRegionDesc = "GalleryCompare("+Globals->algorithm + "," + colEnrolledGallery.flat() + ")"; | |
| 433 | + | |
| 434 | + QScopedPointer<Transform> compareRegion; | |
| 435 | + | |
| 436 | + // If we need to enroll th row set, add the current transform to the aglorithm | |
| 437 | + if (needEnrollRows) | |
| 438 | + { | |
| 439 | + if (!multiProcess) | |
| 440 | + { | |
| 441 | + compareRegionDesc = "Identity+" + compareRegionDesc; | |
| 442 | + compareRegion.reset(Transform::make(compareRegionDesc,NULL)); | |
| 443 | + CompositeTransform * downcast = dynamic_cast<CompositeTransform *> (compareRegion.data()); | |
| 444 | + if (downcast == NULL) | |
| 445 | + qFatal("Pipe downcast failed in compare"); | |
| 446 | + | |
| 447 | + downcast->transforms[0] = this->transform.data(); | |
| 448 | + downcast->init(); | |
| 449 | + } | |
| 450 | + else | |
| 451 | + { | |
| 452 | + compareRegionDesc = "ProcessWrapper(" + this->transformString + "+" + compareRegionDesc + ")"; | |
| 453 | + compareRegion.reset(Transform::make(compareRegionDesc, NULL)); | |
| 454 | + } | |
| 455 | + } | |
| 456 | + else { | |
| 457 | + compareRegion.reset(Transform::make(compareRegionDesc,NULL)); | |
| 458 | + } | |
| 374 | 459 | |
| 375 | - const float speed = 1000 * Globals->totalSteps / Globals->startTime.elapsed() / std::max(1, abs(Globals->parallelism)); | |
| 376 | - if (!Globals->quiet && (Globals->totalSteps > 1)) fprintf(stderr, "\rSPEED=%.1e \n", speed); | |
| 377 | - Globals->totalSteps = 0; | |
| 460 | + compareRegion->init(); | |
| 461 | + | |
| 462 | + // We also need to add Output and progress counting to the algorithm we are building | |
| 463 | + QString joinDesc = "Identity+Identity"; | |
| 464 | + QScopedPointer<Transform> join(Transform::make(joinDesc, NULL)); | |
| 465 | + | |
| 466 | + // The output transform takes the metadata memGalleries we set up previously as input, along with the | |
| 467 | + // output specification we were passed | |
| 468 | + QString outputRegionDesc = "Output("+ output.flat() +"," + targetMetaMem.flat() +"," + queryMetaMem.flat() + ","+ QString::number(transposeCompare ? 1 : 0) + ")"; | |
| 469 | + outputRegionDesc += "+ProgressCounter("+QString::number(rowSize)+")+Discard"; | |
| 470 | + QScopedPointer<Transform> outputTform(Transform::make(outputRegionDesc, NULL)); | |
| 471 | + | |
| 472 | + CompositeTransform * downcast = dynamic_cast<CompositeTransform *> (join.data()); | |
| 473 | + downcast->transforms[0] = compareRegion.data(); | |
| 474 | + downcast->transforms[1] = outputTform.data(); | |
| 475 | + | |
| 476 | + // With this, we have set up a transform which (optionally) enrolls templates, compares them | |
| 477 | + // against a gallery, and outputs them. | |
| 478 | + join->init(); | |
| 479 | + | |
| 480 | + | |
| 481 | + // Now, we will give that base algorithm to a stream, operating in StreamGallery mode | |
| 482 | + QString streamDesc = "Stream(Identity, readMode=StreamGallery)"; | |
| 483 | + QScopedPointer<Transform> streamBase(Transform::make(streamDesc, NULL)); | |
| 484 | + WrapperTransform * streamWrapper = dynamic_cast<WrapperTransform *> (streamBase.data()); | |
| 485 | + streamWrapper->transform = join.data(); | |
| 486 | + | |
| 487 | + streamWrapper->init(); | |
| 488 | + | |
| 489 | + // We set up a template containing the file iwth the row gallery we | |
| 490 | + // want to compare | |
| 491 | + TemplateList rowGalleryTemplate; | |
| 492 | + rowGalleryTemplate.append(Template(rowGallery)); | |
| 493 | + TemplateList outputGallery; | |
| 494 | + | |
| 495 | + // for prgress counting | |
| 496 | + Globals->currentStep = 0; | |
| 497 | + Globals->totalSteps = rowSize; | |
| 498 | + Globals->startTime.start(); | |
| 499 | + | |
| 500 | + // Do the actual comparisons | |
| 501 | + streamWrapper->projectUpdate(rowGalleryTemplate, outputGallery); | |
| 378 | 502 | } |
| 379 | 503 | |
| 380 | 504 | private: |
| ... | ... | @@ -407,10 +531,15 @@ private: |
| 407 | 531 | if ((words.size() < 1) || (words.size() > 2)) qFatal("Invalid algorithm format."); |
| 408 | 532 | //! [Parsing the algorithm description] |
| 409 | 533 | |
| 534 | + transformString = words[0]; | |
| 535 | + | |
| 410 | 536 | |
| 411 | 537 | //! [Creating the template generation and comparison methods] |
| 412 | 538 | transform = QSharedPointer<Transform>(Transform::make(words[0], NULL)); |
| 413 | - if (words.size() > 1) distance = QSharedPointer<Distance>(Distance::make(words[1], NULL)); | |
| 539 | + if (words.size() > 1) { | |
| 540 | + distance = QSharedPointer<Distance>(Distance::make(words[1], NULL)); | |
| 541 | + distanceString = words[1]; | |
| 542 | + } | |
| 414 | 543 | //! [Creating the template generation and comparison methods] |
| 415 | 544 | } |
| 416 | 545 | }; | ... | ... |
openbr/openbr_plugin.cpp
| ... | ... | @@ -1101,13 +1101,19 @@ void Output::initialize(const FileList &targetFiles, const FileList &queryFiles) |
| 1101 | 1101 | { |
| 1102 | 1102 | this->targetFiles = targetFiles; |
| 1103 | 1103 | this->queryFiles = queryFiles; |
| 1104 | + if (this->blockRows == -1) | |
| 1105 | + blockRows = Globals->blockSize; | |
| 1106 | + | |
| 1107 | + if (this->blockCols == -1) | |
| 1108 | + blockCols = Globals->blockSize; | |
| 1109 | + | |
| 1104 | 1110 | selfSimilar = (queryFiles == targetFiles) && (targetFiles.size() > 1) && (queryFiles.size() > 1); |
| 1105 | 1111 | } |
| 1106 | 1112 | |
| 1107 | 1113 | void Output::setBlock(int rowBlock, int columnBlock) |
| 1108 | 1114 | { |
| 1109 | - offset = QPoint((columnBlock == -1) ? 0 : Globals->blockSize*columnBlock, | |
| 1110 | - (rowBlock == -1) ? 0 : Globals->blockSize*rowBlock); | |
| 1115 | + offset = QPoint((columnBlock == -1) ? 0 : blockCols*columnBlock, | |
| 1116 | + (rowBlock == -1) ? 0 : blockRows*rowBlock); | |
| 1111 | 1117 | if (!next.isNull()) next->setBlock(rowBlock, columnBlock); |
| 1112 | 1118 | } |
| 1113 | 1119 | ... | ... |
openbr/openbr_plugin.h
| ... | ... | @@ -996,6 +996,11 @@ class BR_EXPORT Output : public Object |
| 996 | 996 | Q_OBJECT |
| 997 | 997 | |
| 998 | 998 | public: |
| 999 | + Q_PROPERTY(int blockRows READ get_blockRows WRITE set_blockRows RESET reset_blockRows STORED false) | |
| 1000 | + Q_PROPERTY(int blockCols READ get_blockCols WRITE set_blockCols RESET reset_blockCols STORED false) | |
| 1001 | + BR_PROPERTY(int, blockRows, -1) | |
| 1002 | + BR_PROPERTY(int, blockCols, -1) | |
| 1003 | + | |
| 999 | 1004 | FileList targetFiles; /*!< \brief List of files representing the gallery templates. */ |
| 1000 | 1005 | FileList queryFiles; /*!< \brief List of files representing the probe templates. */ |
| 1001 | 1006 | bool selfSimilar; /*!< \brief \c true if the \em targetFiles == \em queryFiles, \c false otherwise. */ |
| ... | ... | @@ -1074,9 +1079,11 @@ public: |
| 1074 | 1079 | */ |
| 1075 | 1080 | class BR_EXPORT Gallery : public Object |
| 1076 | 1081 | { |
| 1077 | - Q_OBJECT | |
| 1078 | - | |
| 1082 | + Q_OBJECT | |
| 1079 | 1083 | public: |
| 1084 | + Q_PROPERTY(int readBlockSize READ get_readBlockSize WRITE set_readBlockSize RESET reset_readBlockSize STORED false) | |
| 1085 | + BR_PROPERTY(int, readBlockSize, Globals->blockSize) | |
| 1086 | + | |
| 1080 | 1087 | virtual ~Gallery() {} |
| 1081 | 1088 | TemplateList read(); /*!< \brief Retrieve all the stored templates. */ |
| 1082 | 1089 | FileList files(); /*!< \brief Retrieve all the stored template files. */ | ... | ... |
openbr/plugins/distance.cpp
| ... | ... | @@ -460,12 +460,13 @@ BR_REGISTER(Distance, SumDistance) |
| 460 | 460 | class GalleryCompareTransform : public Transform |
| 461 | 461 | { |
| 462 | 462 | Q_OBJECT |
| 463 | - Q_PROPERTY(br::Distance *distance READ get_distance WRITE set_distance RESET reset_distance STORED false) | |
| 463 | + Q_PROPERTY(QString distanceAlgorithm READ get_distanceAlgorithm WRITE set_distanceAlgorithm RESET reset_distanceAlgorithm STORED false) | |
| 464 | 464 | Q_PROPERTY(QString galleryName READ get_galleryName WRITE set_galleryName RESET reset_galleryName STORED false) |
| 465 | - BR_PROPERTY(br::Distance*, distance, NULL) | |
| 465 | + BR_PROPERTY(QString, distanceAlgorithm, "") | |
| 466 | 466 | BR_PROPERTY(QString, galleryName, "") |
| 467 | 467 | |
| 468 | 468 | TemplateList gallery; |
| 469 | + QSharedPointer<Distance> distance; | |
| 469 | 470 | |
| 470 | 471 | void project(const Template &src, Template &dst) const |
| 471 | 472 | { |
| ... | ... | @@ -479,8 +480,13 @@ class GalleryCompareTransform : public Transform |
| 479 | 480 | |
| 480 | 481 | void init() |
| 481 | 482 | { |
| 482 | - if (!galleryName.isEmpty()) | |
| 483 | + if (!galleryName.isEmpty()) { | |
| 483 | 484 | gallery = TemplateList::fromGallery(galleryName); |
| 485 | + } | |
| 486 | + if (!distanceAlgorithm.isEmpty()) | |
| 487 | + { | |
| 488 | + distance = Distance::fromAlgorithm(distanceAlgorithm); | |
| 489 | + } | |
| 484 | 490 | } |
| 485 | 491 | }; |
| 486 | 492 | ... | ... |
openbr/plugins/gallery.cpp
| ... | ... | @@ -123,7 +123,7 @@ class galGallery : public Gallery |
| 123 | 123 | gallery.seek(0); |
| 124 | 124 | |
| 125 | 125 | TemplateList templates; |
| 126 | - while ((templates.size() < Globals->blockSize) && !stream.atEnd()) { | |
| 126 | + while ((templates.size() < readBlockSize) && !stream.atEnd()) { | |
| 127 | 127 | Template m; |
| 128 | 128 | stream >> m; |
| 129 | 129 | templates.append(m); |
| ... | ... | @@ -348,8 +348,8 @@ class memGallery : public Gallery |
| 348 | 348 | MemoryGalleries::aligned[file] = true; |
| 349 | 349 | } |
| 350 | 350 | |
| 351 | - TemplateList templates = MemoryGalleries::galleries[file].mid(block*Globals->blockSize, Globals->blockSize); | |
| 352 | - *done = (templates.size() < Globals->blockSize); | |
| 351 | + TemplateList templates = MemoryGalleries::galleries[file].mid(block*readBlockSize, readBlockSize); | |
| 352 | + *done = (templates.size() < readBlockSize); | |
| 353 | 353 | block = *done ? 0 : block+1; |
| 354 | 354 | return templates; |
| 355 | 355 | } | ... | ... |
openbr/plugins/misc.cpp
| ... | ... | @@ -552,6 +552,146 @@ public: |
| 552 | 552 | |
| 553 | 553 | BR_REGISTER(Transform, ProgressCounterTransform) |
| 554 | 554 | |
| 555 | + | |
| 556 | +class OutputTransform : public TimeVaryingTransform | |
| 557 | +{ | |
| 558 | + Q_OBJECT | |
| 559 | + | |
| 560 | + Q_PROPERTY(QString outputString READ get_outputString WRITE set_outputString RESET reset_outputString STORED false) | |
| 561 | + // names of mem galleries containing filelists we need. | |
| 562 | + Q_PROPERTY(QString targetName READ get_targetName WRITE set_targetName RESET reset_targetName STORED false) | |
| 563 | + Q_PROPERTY(QString queryName READ get_queryName WRITE set_queryName RESET reset_queryName STORED false) | |
| 564 | + Q_PROPERTY(bool transposeMode READ get_transposeMode WRITE set_transposeMode RESET reset_transposeMode STORED false) | |
| 565 | + | |
| 566 | + BR_PROPERTY(QString, outputString, "") | |
| 567 | + BR_PROPERTY(QString, targetName, "") | |
| 568 | + BR_PROPERTY(QString, queryName, "") | |
| 569 | + | |
| 570 | + BR_PROPERTY(bool,transposeMode, false) | |
| 571 | + ; | |
| 572 | + | |
| 573 | + void projectUpdate(const TemplateList &src, TemplateList &dst) | |
| 574 | + { | |
| 575 | + dst = src; | |
| 576 | + | |
| 577 | + if (src.empty()) | |
| 578 | + return; | |
| 579 | + | |
| 580 | + // we received a template, which is the next row/column in order | |
| 581 | + foreach(const Template & t, dst) { | |
| 582 | + for (int i=0; i < t.m().cols; i++) | |
| 583 | + { | |
| 584 | + output->setRelative(t.m().at<float>(0, i), currentRow, currentCol); | |
| 585 | + | |
| 586 | + // row-major input | |
| 587 | + if (!transposeMode) | |
| 588 | + currentCol++; | |
| 589 | + // col-major input | |
| 590 | + else | |
| 591 | + currentRow++; | |
| 592 | + } | |
| 593 | + // filled in a row, advance to the next, reset column position | |
| 594 | + if (!transposeMode) { | |
| 595 | + currentRow++; | |
| 596 | + currentCol = 0; | |
| 597 | + } | |
| 598 | + // filled in a column, advance, reset row | |
| 599 | + else { | |
| 600 | + currentCol++; | |
| 601 | + currentRow = 0; | |
| 602 | + } | |
| 603 | + } | |
| 604 | + | |
| 605 | + bool blockDone = false; | |
| 606 | + // In direct mode, we don't buffer rows | |
| 607 | + if (!transposeMode) | |
| 608 | + { | |
| 609 | + currentBlockRow++; | |
| 610 | + blockDone = true; | |
| 611 | + } | |
| 612 | + // in transpose mode, we buffer 100 cols before writing the block | |
| 613 | + else if (currentCol == bufferedSize) | |
| 614 | + { | |
| 615 | + currentBlockCol++; | |
| 616 | + blockDone = true; | |
| 617 | + } | |
| 618 | + else return; | |
| 619 | + | |
| 620 | + if (blockDone) | |
| 621 | + { | |
| 622 | + // set the next block, only necessary if we haven't buffered the current item | |
| 623 | + output->setBlock(currentBlockRow, currentBlockCol); | |
| 624 | + currentRow = 0; | |
| 625 | + currentCol = 0; | |
| 626 | + } | |
| 627 | + } | |
| 628 | + | |
| 629 | + void train(const TemplateList& data) | |
| 630 | + { | |
| 631 | + (void) data; | |
| 632 | + } | |
| 633 | + | |
| 634 | + void init() | |
| 635 | + { | |
| 636 | + if (targetName.isEmpty() || queryName.isEmpty() || outputString.isEmpty()) | |
| 637 | + return; | |
| 638 | + | |
| 639 | + QScopedPointer<Gallery> tGallery(Gallery::make(targetName)); | |
| 640 | + QScopedPointer<Gallery> qGallery(Gallery::make(queryName)); | |
| 641 | + | |
| 642 | + FileList targetFiles = tGallery->files(); | |
| 643 | + FileList queryFiles = qGallery->files(); | |
| 644 | + | |
| 645 | + currentBlockRow = 0; | |
| 646 | + currentBlockCol = 0; | |
| 647 | + | |
| 648 | + currentRow = 0; | |
| 649 | + currentCol = 0; | |
| 650 | + | |
| 651 | + bufferedSize = 100; | |
| 652 | + | |
| 653 | + if (transposeMode) | |
| 654 | + { | |
| 655 | + // buffer 100 cols at a time | |
| 656 | + fragmentsPerRow = bufferedSize; | |
| 657 | + // a single col contains comparisons to all query files | |
| 658 | + fragmentsPerCol = queryFiles.size(); | |
| 659 | + } | |
| 660 | + else | |
| 661 | + { | |
| 662 | + // a single row contains comparisons to all target files | |
| 663 | + fragmentsPerRow = targetFiles.size(); | |
| 664 | + // we output rows one at a time | |
| 665 | + fragmentsPerCol = 1; | |
| 666 | + } | |
| 667 | + | |
| 668 | + output = QSharedPointer<Output>(Output::make(outputString, targetFiles, queryFiles)); | |
| 669 | + output->blockRows = fragmentsPerCol; | |
| 670 | + output->blockCols = fragmentsPerRow; | |
| 671 | + output->initialize(targetFiles, queryFiles); | |
| 672 | + | |
| 673 | + output->setBlock(currentBlockRow, currentBlockCol); | |
| 674 | + } | |
| 675 | + | |
| 676 | + QSharedPointer<Output> output; | |
| 677 | + | |
| 678 | + int bufferedSize; | |
| 679 | + | |
| 680 | + int currentRow; | |
| 681 | + int currentCol; | |
| 682 | + | |
| 683 | + int currentBlockRow; | |
| 684 | + int currentBlockCol; | |
| 685 | + | |
| 686 | + int fragmentsPerRow; | |
| 687 | + int fragmentsPerCol; | |
| 688 | + | |
| 689 | +public: | |
| 690 | + OutputTransform() : TimeVaryingTransform(false,false) {} | |
| 691 | +}; | |
| 692 | + | |
| 693 | +BR_REGISTER(Transform, OutputTransform) | |
| 694 | + | |
| 555 | 695 | } |
| 556 | 696 | |
| 557 | 697 | #include "misc.moc" | ... | ... |
openbr/plugins/output.cpp
| ... | ... | @@ -215,9 +215,11 @@ class mtxOutput : public Output |
| 215 | 215 | |
| 216 | 216 | this->rowBlock = rowBlock; |
| 217 | 217 | this->columnBlock = columnBlock; |
| 218 | - blockScores = cv::Mat(std::min(queryFiles.size()-rowBlock*Globals->blockSize, Globals->blockSize), | |
| 219 | - std::min(targetFiles.size()-columnBlock*Globals->blockSize, Globals->blockSize), | |
| 220 | - CV_32FC1); | |
| 218 | + | |
| 219 | + int matrixRows = std::min(queryFiles.size()-rowBlock*this->blockRows, blockRows); | |
| 220 | + int matrixCols = std::min(targetFiles.size()-columnBlock*this->blockCols, blockCols); | |
| 221 | + | |
| 222 | + blockScores = cv::Mat(matrixRows, matrixCols, CV_32FC1); | |
| 221 | 223 | } |
| 222 | 224 | |
| 223 | 225 | void setRelative(float value, int i, int j) |
| ... | ... | @@ -237,7 +239,7 @@ class mtxOutput : public Output |
| 237 | 239 | if (!f.open(QFile::ReadWrite)) |
| 238 | 240 | qFatal("Unable to open %s for modifying.", qPrintable(file)); |
| 239 | 241 | for (int i=0; i<blockScores.rows; i++) { |
| 240 | - f.seek(headerSize + sizeof(float)*(quint64(rowBlock*Globals->blockSize+i)*targetFiles.size()+(columnBlock*Globals->blockSize))); | |
| 242 | + f.seek(headerSize + sizeof(float)*(quint64(rowBlock*this->blockRows+i)*targetFiles.size()+(columnBlock*this->blockCols))); | |
| 241 | 243 | f.write((const char*)blockScores.row(i).data, sizeof(float)*blockScores.cols); |
| 242 | 244 | } |
| 243 | 245 | f.close(); | ... | ... |
openbr/plugins/stream.cpp
| ... | ... | @@ -25,6 +25,7 @@ class Idiocy : public QObject |
| 25 | 25 | public: |
| 26 | 26 | enum StreamModes { StreamVideo, |
| 27 | 27 | DistributeFrames, |
| 28 | + StreamGallery, | |
| 28 | 29 | Auto}; |
| 29 | 30 | |
| 30 | 31 | Q_ENUMS(StreamModes) |
| ... | ... | @@ -288,6 +289,75 @@ protected: |
| 288 | 289 | }; |
| 289 | 290 | |
| 290 | 291 | |
| 292 | +class StreamGallery : public TemplateProcessor | |
| 293 | +{ | |
| 294 | +public: | |
| 295 | + StreamGallery() | |
| 296 | + { | |
| 297 | + | |
| 298 | + } | |
| 299 | + | |
| 300 | + bool open(Template &input) | |
| 301 | + { | |
| 302 | + // Create a gallery | |
| 303 | + gallery = QSharedPointer<Gallery>(Gallery::make(input.file)); | |
| 304 | + // Failed ot open the gallery? | |
| 305 | + if (gallery.isNull()) { | |
| 306 | + qDebug()<<"Failed to create gallery!"; | |
| 307 | + galleryOk = false; | |
| 308 | + return false; | |
| 309 | + } | |
| 310 | + | |
| 311 | + // Set up state variables for future reads | |
| 312 | + galleryOk = true; | |
| 313 | + gallery->set_readBlockSize(100); | |
| 314 | + nextIdx = 0; | |
| 315 | + lastBlock = false; | |
| 316 | + return galleryOk; | |
| 317 | + } | |
| 318 | + | |
| 319 | + bool isOpen() { return galleryOk; } | |
| 320 | + | |
| 321 | + void close() | |
| 322 | + { | |
| 323 | + galleryOk = false; | |
| 324 | + currentData.clear(); | |
| 325 | + nextIdx = 0; | |
| 326 | + lastBlock = true; | |
| 327 | + } | |
| 328 | + | |
| 329 | + bool getNextTemplate(Template & output) | |
| 330 | + { | |
| 331 | + // If we still have data available, we return one of those | |
| 332 | + if (nextIdx >= currentData.size()) | |
| 333 | + { | |
| 334 | + // Otherwise, read another block | |
| 335 | + if (!lastBlock) { | |
| 336 | + currentData = gallery->readBlock(&lastBlock); | |
| 337 | + nextIdx = 0; | |
| 338 | + } | |
| 339 | + else | |
| 340 | + { | |
| 341 | + galleryOk = false; | |
| 342 | + return false; | |
| 343 | + } | |
| 344 | + } | |
| 345 | + // Return the indicated template, and advance the index | |
| 346 | + output = currentData[nextIdx++]; | |
| 347 | + return true; | |
| 348 | + } | |
| 349 | + | |
| 350 | +protected: | |
| 351 | + | |
| 352 | + QSharedPointer<Gallery> gallery; | |
| 353 | + bool galleryOk; | |
| 354 | + bool lastBlock; | |
| 355 | + | |
| 356 | + TemplateList currentData; | |
| 357 | + int nextIdx; | |
| 358 | + | |
| 359 | +}; | |
| 360 | + | |
| 291 | 361 | class DirectReturn : public TemplateProcessor |
| 292 | 362 | { |
| 293 | 363 | public: |
| ... | ... | @@ -731,6 +801,11 @@ protected: |
| 731 | 801 | if (!frameSource) |
| 732 | 802 | frameSource = new DirectReturn(); |
| 733 | 803 | } |
| 804 | + else if (mode == br::Idiocy::StreamGallery) | |
| 805 | + { | |
| 806 | + if (!frameSource) | |
| 807 | + frameSource = new StreamGallery(); | |
| 808 | + } | |
| 734 | 809 | else if (mode == br::Idiocy::StreamVideo) |
| 735 | 810 | { |
| 736 | 811 | if (!frameSource) { | ... | ... |