Commit dff99e3e7bc9947e4820a099e2312b6b8a8015f2
Merge branch 'master' of https://github.com/biometrics/openbr
Showing
24 changed files
with
532 additions
and
88 deletions
3rdparty/stasm4.0.0/stasm/asm.cpp
3rdparty/stasm4.0.0/stasm/faceroi.cpp
3rdparty/stasm4.0.0/stasm/pinstart.cpp
3rdparty/stasm4.0.0/stasm/stasm_lib.cpp
app/br/br.cpp
| ... | ... | @@ -138,8 +138,8 @@ public: |
| 138 | 138 | check(parc >= 2 && parc <= 4, "Incorrect parameter count for 'evalClassification'."); |
| 139 | 139 | br_eval_classification(parv[0], parv[1], parc >= 3 ? parv[2] : "", parc >= 4 ? parv[3] : ""); |
| 140 | 140 | } else if (!strcmp(fun, "evalClustering")) { |
| 141 | - check(parc == 2, "Incorrect parameter count for 'evalClustering'."); | |
| 142 | - br_eval_clustering(parv[0], parv[1]); | |
| 141 | + check((parc >= 2) && (parc <= 3), "Incorrect parameter count for 'evalClustering'."); | |
| 142 | + br_eval_clustering(parv[0], parv[1], parc == 3 ? parv[2] : ""); | |
| 143 | 143 | } else if (!strcmp(fun, "evalDetection")) { |
| 144 | 144 | check((parc >= 2) && (parc <= 3), "Incorrect parameter count for 'evalDetection'."); |
| 145 | 145 | br_eval_detection(parv[0], parv[1], parc == 3 ? parv[2] : ""); | ... | ... |
openbr/core/cluster.cpp
| ... | ... | @@ -100,7 +100,9 @@ Neighborhood getNeighborhood(const QStringList &simmats) |
| 100 | 100 | int currentRows = -1; |
| 101 | 101 | int columnOffset = 0; |
| 102 | 102 | for (int j=0; j<numGalleries; j++) { |
| 103 | - cv::Mat m = BEE::readMat(simmats[i*numGalleries+j]); | |
| 103 | + QScopedPointer<br::Format> format(br::Factory<br::Format>::make(simmats[i*numGalleries+j])); | |
| 104 | + br::Template t = format->read(); | |
| 105 | + cv::Mat m = t.m(); | |
| 104 | 106 | if (j==0) { |
| 105 | 107 | currentRows = m.rows; |
| 106 | 108 | allNeighbors.resize(currentRows); |
| ... | ... | @@ -115,8 +117,9 @@ Neighborhood getNeighborhood(const QStringList &simmats) |
| 115 | 117 | float val = m.at<float>(k,l); |
| 116 | 118 | if ((i==j) && (k==l)) continue; // Skips self-similarity scores |
| 117 | 119 | |
| 118 | - if ((val != -std::numeric_limits<float>::infinity()) && | |
| 119 | - (val != std::numeric_limits<float>::infinity())) { | |
| 120 | + if (val != -std::numeric_limits<float>::max() | |
| 121 | + && val != -std::numeric_limits<float>::infinity() | |
| 122 | + && val != std::numeric_limits<float>::infinity()) { | |
| 120 | 123 | globalMax = std::max(globalMax, val); |
| 121 | 124 | globalMin = std::min(globalMin, val); |
| 122 | 125 | } |
| ... | ... | @@ -157,7 +160,7 @@ Neighborhood getNeighborhood(const QStringList &simmats) |
| 157 | 160 | // Zhu et al. "A Rank-Order Distance based Clustering Algorithm for Face Tagging", CVPR 2011 |
| 158 | 161 | br::Clusters br::ClusterGallery(const QStringList &simmats, float aggressiveness, const QString &csv) |
| 159 | 162 | { |
| 160 | - qDebug("Clustering %d simmat(s)", simmats.size()); | |
| 163 | + qDebug("Clustering %d simmat(s), aggressiveness %f", simmats.size(), aggressiveness); | |
| 161 | 164 | |
| 162 | 165 | // Read in gallery parts, keeping top neighbors of each template |
| 163 | 166 | Neighborhood neighborhood = getNeighborhood(simmats); |
| ... | ... | @@ -275,13 +278,14 @@ float jaccardIndex(const QVector<int> &indicesA, const QVector<int> &indicesB) |
| 275 | 278 | |
| 276 | 279 | // Evaluates clustering algorithms based on metrics described in |
| 277 | 280 | // Santo Fortunato "Community detection in graphs", Physics Reports 486 (2010) |
| 278 | -void br::EvalClustering(const QString &csv, const QString &input) | |
| 281 | +void br::EvalClustering(const QString &csv, const QString &input, QString truth_property) | |
| 279 | 282 | { |
| 283 | + if (truth_property.isEmpty()) | |
| 284 | + truth_property = "Label"; | |
| 280 | 285 | qDebug("Evaluating %s against %s", qPrintable(csv), qPrintable(input)); |
| 281 | 286 | |
| 282 | - // We assume clustering algorithms store assigned cluster labels as integers (since the clusters are | |
| 283 | - // not named). Direct use of ClusterID is not general -cao | |
| 284 | - QList<int> labels = File::get<int>(TemplateList::fromGallery(input), "ClusterID"); | |
| 287 | + TemplateList tList = TemplateList::fromGallery(input); | |
| 288 | + QList<int> labels = tList.indexProperty(truth_property); | |
| 285 | 289 | |
| 286 | 290 | QHash<int, int> labelToIndex; |
| 287 | 291 | int nClusters = 0; | ... | ... |
openbr/core/cluster.h
| ... | ... | @@ -28,7 +28,7 @@ namespace br |
| 28 | 28 | typedef QVector<Cluster> Clusters; |
| 29 | 29 | |
| 30 | 30 | Clusters ClusterGallery(const QStringList &simmats, float aggressiveness, const QString &csv); |
| 31 | - void EvalClustering(const QString &csv, const QString &input); | |
| 31 | + void EvalClustering(const QString &csv, const QString &input, QString truth_property); | |
| 32 | 32 | |
| 33 | 33 | Clusters ReadClusters(const QString &csv); |
| 34 | 34 | void WriteClusters(const Clusters &clusters, const QString &csv); | ... | ... |
openbr/core/core.cpp
| ... | ... | @@ -21,14 +21,16 @@ |
| 21 | 21 | #include "qtutils.h" |
| 22 | 22 | #include "../plugins/openbr_internal.h" |
| 23 | 23 | |
| 24 | -using namespace br; | |
| 24 | +namespace br { | |
| 25 | 25 | |
| 26 | -/**** ALGORITHM_CORE ****/ | |
| 27 | 26 | struct AlgorithmCore |
| 28 | 27 | { |
| 29 | 28 | QSharedPointer<Transform> transform; |
| 30 | 29 | QSharedPointer<Distance> distance; |
| 31 | 30 | |
| 31 | + QString transformString; | |
| 32 | + QString distanceString; | |
| 33 | + | |
| 32 | 34 | AlgorithmCore(const QString &name) |
| 33 | 35 | { |
| 34 | 36 | this->name = name; |
| ... | ... | @@ -134,6 +136,8 @@ struct AlgorithmCore |
| 134 | 136 | } |
| 135 | 137 | TemplateList data(TemplateList::fromGallery(input)); |
| 136 | 138 | |
| 139 | + bool multiProcess = Globals->file.getBool("multiProcess", false); | |
| 140 | + | |
| 137 | 141 | if (gallery.contains("append")) |
| 138 | 142 | { |
| 139 | 143 | // Remove any templates which are already in the gallery |
| ... | ... | @@ -155,20 +159,27 @@ struct AlgorithmCore |
| 155 | 159 | Globals->currentStep = 0; |
| 156 | 160 | Globals->totalSteps = data.length(); |
| 157 | 161 | |
| 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)); | |
| 162 | + QScopedPointer<Transform> basePipe; | |
| 162 | 163 | |
| 163 | - CompositeTransform * downcast = dynamic_cast<CompositeTransform *>(basePipe.data()); | |
| 164 | - if (downcast == NULL) | |
| 165 | - qFatal("downcast failed?"); | |
| 164 | + if (!multiProcess) | |
| 165 | + { | |
| 166 | + QString pipeDesc = "Identity+GalleryOutput("+gallery.flat()+")+ProgressCounter("+QString::number(data.length())+")+Discard"; | |
| 167 | + basePipe.reset(Transform::make(pipeDesc,NULL)); | |
| 168 | + CompositeTransform * downcast = dynamic_cast<CompositeTransform *>(basePipe.data()); | |
| 169 | + if (downcast == NULL) | |
| 170 | + qFatal("downcast failed?"); | |
| 166 | 171 | |
| 167 | - // replace that placeholder with the current algorithm | |
| 168 | - downcast->transforms[0] = this->transform.data(); | |
| 172 | + // replace that placeholder with the current algorithm | |
| 173 | + downcast->transforms[0] = this->transform.data(); | |
| 169 | 174 | |
| 170 | - // call init on the pipe to collapse the algorithm (if its top level is a pipe) | |
| 171 | - downcast->init(); | |
| 175 | + // call init on the pipe to collapse the algorithm (if its top level is a pipe) | |
| 176 | + downcast->init(); | |
| 177 | + } | |
| 178 | + else | |
| 179 | + { | |
| 180 | + QString pipeDesc = "ProcessWrapper("+transformString+")"+"+GalleryOutput("+gallery.flat()+")+ProgressCounter("+QString::number(data.length())+")+Discard"; | |
| 181 | + basePipe.reset(Transform::make(pipeDesc,NULL)); | |
| 182 | + } | |
| 172 | 183 | |
| 173 | 184 | // Next, we make a Stream (with placeholder transform) |
| 174 | 185 | QString streamDesc = "Stream(Identity, readMode=DistributeFrames)"; |
| ... | ... | @@ -176,7 +187,7 @@ struct AlgorithmCore |
| 176 | 187 | WrapperTransform * wrapper = dynamic_cast<WrapperTransform *> (baseStream.data()); |
| 177 | 188 | |
| 178 | 189 | // replace that placeholder with the pipe we built |
| 179 | - wrapper->transform = downcast; | |
| 190 | + wrapper->transform = basePipe.data(); | |
| 180 | 191 | |
| 181 | 192 | // and get the final stream's stages by reinterpreting the pipe. Perfectly straightforward. |
| 182 | 193 | wrapper->init(); |
| ... | ... | @@ -241,17 +252,14 @@ struct AlgorithmCore |
| 241 | 252 | dummyTarget.append(targets[0]); |
| 242 | 253 | QScopedPointer<Output> realOutput(Output::make(output, dummyTarget, queryFiles)); |
| 243 | 254 | |
| 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; | |
| 255 | + realOutput->set_blockRows(INT_MAX); | |
| 256 | + realOutput->set_blockCols(INT_MAX); | |
| 247 | 257 | realOutput->setBlock(0,0); |
| 248 | 258 | for (int i=0; i < queries.length(); i++) |
| 249 | 259 | { |
| 250 | 260 | float res = distance->compare(queries[i], targets[i]); |
| 251 | 261 | realOutput->setRelative(res, 0,i); |
| 252 | 262 | } |
| 253 | - | |
| 254 | - Globals->blockSize = old_block_size; | |
| 255 | 263 | } |
| 256 | 264 | |
| 257 | 265 | void deduplicate(const File &inputGallery, const File &outputGallery, const float threshold) |
| ... | ... | @@ -310,7 +318,11 @@ struct AlgorithmCore |
| 310 | 318 | qPrintable(queryGallery.flat()), |
| 311 | 319 | output.isNull() ? "" : qPrintable(" to " + output.flat())); |
| 312 | 320 | |
| 313 | - if (output.exists() && output.get<bool>("cache", false)) return; | |
| 321 | + // Escape hatch for distances that need to operate directly on the gallery files | |
| 322 | + if (distance->compare(targetGallery, queryGallery, output)) | |
| 323 | + return; | |
| 324 | + | |
| 325 | + if (output.exists() && output.get<bool>("cache")) return; | |
| 314 | 326 | if (queryGallery == ".") queryGallery = targetGallery; |
| 315 | 327 | |
| 316 | 328 | QScopedPointer<Gallery> t, q; |
| ... | ... | @@ -327,11 +339,13 @@ struct AlgorithmCore |
| 327 | 339 | File splitOutputFile = output.name.arg(i); |
| 328 | 340 | outputFiles.append(splitOutputFile); |
| 329 | 341 | } |
| 342 | + } else { | |
| 343 | + outputFiles.append(output); | |
| 330 | 344 | } |
| 331 | - else outputFiles.append(output); | |
| 332 | 345 | |
| 333 | 346 | QList<Output*> outputs; |
| 334 | - foreach (const File &outputFile, outputFiles) outputs.append(Output::make(outputFile, targetFiles, queryFiles)); | |
| 347 | + foreach (const File &outputFile, outputFiles) | |
| 348 | + outputs.append(Output::make(outputFile, targetFiles, queryFiles)); | |
| 335 | 349 | |
| 336 | 350 | if (distance.isNull()) qFatal("Null distance."); |
| 337 | 351 | Globals->currentStep = 0; |
| ... | ... | @@ -361,7 +375,6 @@ struct AlgorithmCore |
| 361 | 375 | else targetPartitions.append(targets); |
| 362 | 376 | |
| 363 | 377 | outputs[i]->setBlock(queryBlock, targetBlock); |
| 364 | - | |
| 365 | 378 | distance->compare(targetPartitions[i], queryPartitions[i], outputs[i]); |
| 366 | 379 | |
| 367 | 380 | Globals->currentStep += double(targets.size()) * double(queries.size()); |
| ... | ... | @@ -407,14 +420,22 @@ private: |
| 407 | 420 | if ((words.size() < 1) || (words.size() > 2)) qFatal("Invalid algorithm format."); |
| 408 | 421 | //! [Parsing the algorithm description] |
| 409 | 422 | |
| 423 | + transformString = words[0]; | |
| 424 | + | |
| 410 | 425 | |
| 411 | 426 | //! [Creating the template generation and comparison methods] |
| 412 | 427 | transform = QSharedPointer<Transform>(Transform::make(words[0], NULL)); |
| 413 | - if (words.size() > 1) distance = QSharedPointer<Distance>(Distance::make(words[1], NULL)); | |
| 428 | + if (words.size() > 1) { | |
| 429 | + distance = QSharedPointer<Distance>(Distance::make(words[1], NULL)); | |
| 430 | + distanceString = words[1]; | |
| 431 | + } | |
| 414 | 432 | //! [Creating the template generation and comparison methods] |
| 415 | 433 | } |
| 416 | 434 | }; |
| 417 | 435 | |
| 436 | +} // namespace br | |
| 437 | + | |
| 438 | +using namespace br; | |
| 418 | 439 | |
| 419 | 440 | class AlgorithmManager : public Initializer |
| 420 | 441 | { | ... | ... |
openbr/core/plot.cpp
| ... | ... | @@ -247,7 +247,7 @@ bool Plot(const QStringList &files, const File &destination, bool show) |
| 247 | 247 | (p.major.size > 1 ? getScale("colour", p.major.header, p.major.size) : QString()) + |
| 248 | 248 | (p.minor.size > 1 ? QString(" + scale_linetype_discrete(\"%1\")").arg(p.minor.header) : QString()) + |
| 249 | 249 | QString(" + scale_x_log10(labels=trans_format(\"log10\", math_format()))") + |
| 250 | - (rocOpts.contains("yLimits") ? QString(" + scale_y_continuous(labels=percent, limits=%3)").arg("c"+QtUtils::toString(rocOpts.get<QPointF>("yLimits",QPointF()))) : QString(" + scale_y_continuous(labels=percent)")) + | |
| 250 | + (rocOpts.contains("yLimits") ? QString(" + scale_y_continuous(labels=percent) + coord_cartesian(ylim=%1)").arg("c"+QtUtils::toString(rocOpts.get<QPointF>("yLimits",QPointF()))) : QString(" + scale_y_continuous(labels=percent)")) + | |
| 251 | 251 | QString(" + annotation_logticks(sides=\"b\")") + |
| 252 | 252 | QString(" + theme(legend.title = element_text(size = %1), plot.title = element_text(size = %1), axis.text = element_text(size = %1), axis.title.x = element_text(size = %1), axis.title.y = element_text(size = %1)," |
| 253 | 253 | " legend.position=%2, legend.background = element_rect(fill = 'white'), panel.grid.major = element_line(colour = \"gray\"), panel.grid.minor = element_line(colour = \"gray\", linetype = \"dashed\"), legend.text = element_text(size = %1))\n\n").arg(QString::number(rocOpts.get<float>("textSize",12)), rocOpts.contains("legendPosition") ? "c"+QtUtils::toString(rocOpts.get<QPointF>("legendPosition")) : "'right'"))); |
| ... | ... | @@ -273,7 +273,7 @@ bool Plot(const QStringList &files, const File &destination, bool show) |
| 273 | 273 | (minimalist ? "" : " + scale_x_log10(labels=c(1,5,10,50,100), breaks=c(1,5,10,50,100)) + annotation_logticks(sides=\"b\")") + |
| 274 | 274 | (p.major.size > 1 ? getScale("colour", p.major.header, p.major.size) : QString()) + |
| 275 | 275 | (p.minor.size > 1 ? QString(" + scale_linetype_discrete(\"%1\")").arg(p.minor.header) : QString()) + |
| 276 | - (cmcOpts.contains("yLimits") ? QString(" + scale_y_continuous(labels=percent, limits=%3)").arg("c"+QtUtils::toString(cmcOpts.get<QPointF>("yLimits",QPointF()))) : QString(" + scale_y_continuous(labels=percent)")) + | |
| 276 | + (cmcOpts.contains("yLimits") ? QString(" + scale_y_continuous(labels=percent) + coord_cartesian(ylim=%1)").arg("c"+QtUtils::toString(cmcOpts.get<QPointF>("yLimits",QPointF()))) : QString(" + scale_y_continuous(labels=percent)")) + | |
| 277 | 277 | QString(" + theme_minimal() + theme(legend.title = element_text(size = %1), plot.title = element_text(size = %1), axis.text = element_text(size = %1), axis.title.x = element_text(size = %1), axis.title.y = element_text(size = %1)," |
| 278 | 278 | " legend.position=%2, legend.background = element_rect(fill = 'white'), panel.grid.major = element_line(colour = \"gray\"), panel.grid.minor = element_line(colour = \"gray\", linetype = \"dashed\"), legend.text = element_text(size = %1))\n\n").arg(QString::number(cmcOpts.get<float>("textSize",12)), cmcOpts.contains("legendPosition") ? "c"+QtUtils::toString(cmcOpts.get<QPointF>("legendPosition")) : "'right'"))); |
| 279 | 279 | ... | ... |
openbr/openbr.cpp
| ... | ... | @@ -109,9 +109,9 @@ void br_eval_classification(const char *predicted_gallery, const char *truth_gal |
| 109 | 109 | EvalClassification(predicted_gallery, truth_gallery, predicted_property, truth_property); |
| 110 | 110 | } |
| 111 | 111 | |
| 112 | -void br_eval_clustering(const char *csv, const char *gallery) | |
| 112 | +void br_eval_clustering(const char *csv, const char *gallery, const char * truth_property) | |
| 113 | 113 | { |
| 114 | - EvalClustering(csv, gallery); | |
| 114 | + EvalClustering(csv, gallery, truth_property); | |
| 115 | 115 | } |
| 116 | 116 | |
| 117 | 117 | float br_eval_detection(const char *predicted_gallery, const char *truth_gallery, const char *csv) |
| ... | ... | @@ -435,7 +435,14 @@ br_template_list br_load_from_gallery(br_gallery gallery) |
| 435 | 435 | return (br_template_list)tl; |
| 436 | 436 | } |
| 437 | 437 | |
| 438 | -void br_add_to_gallery(br_gallery gallery, br_template_list tl) | |
| 438 | +void br_add_template_to_gallery(br_gallery gallery, br_template tmpl) | |
| 439 | +{ | |
| 440 | + Gallery *gal = reinterpret_cast<Gallery*>(gallery); | |
| 441 | + Template *t = reinterpret_cast<Template*>(tmpl); | |
| 442 | + gal->write(*t); | |
| 443 | +} | |
| 444 | + | |
| 445 | +void br_add_template_list_to_gallery(br_gallery gallery, br_template_list tl) | |
| 439 | 446 | { |
| 440 | 447 | Gallery *gal = reinterpret_cast<Gallery*>(gallery); |
| 441 | 448 | TemplateList *realTL = reinterpret_cast<TemplateList*>(tl); | ... | ... |
openbr/openbr.h
| ... | ... | @@ -167,9 +167,10 @@ BR_EXPORT void br_eval_classification(const char *predicted_gallery, const char |
| 167 | 167 | * \brief Evaluates and prints clustering accuracy to the terminal. |
| 168 | 168 | * \param csv The cluster results file. |
| 169 | 169 | * \param gallery The br::Gallery used to generate the \ref simmat that was clustered. |
| 170 | + * \param truth_property (Optional) which metadata key to use from <i>gallery</i/>, defaults to Label | |
| 170 | 171 | * \see br_cluster |
| 171 | 172 | */ |
| 172 | -BR_EXPORT void br_eval_clustering(const char *csv, const char *gallery); | |
| 173 | +BR_EXPORT void br_eval_clustering(const char *csv, const char *gallery, const char * truth_property); | |
| 173 | 174 | |
| 174 | 175 | /*! |
| 175 | 176 | * \brief Evaluates and prints detection accuracy to terminal. |
| ... | ... | @@ -562,9 +563,13 @@ BR_EXPORT br_gallery br_make_gallery(const char *gallery); |
| 562 | 563 | */ |
| 563 | 564 | BR_EXPORT br_template_list br_load_from_gallery(br_gallery gallery); |
| 564 | 565 | /*! |
| 566 | + * \brief Write a br::Template to the br::Gallery on disk. | |
| 567 | + */ | |
| 568 | +BR_EXPORT void br_add_template_to_gallery(br_gallery gallery, br_template tmpl); | |
| 569 | +/*! | |
| 565 | 570 | * \brief Write a br::TemplateList to the br::Gallery on disk. |
| 566 | 571 | */ |
| 567 | -BR_EXPORT void br_add_to_gallery(br_gallery gallery, br_template_list tl); | |
| 572 | +BR_EXPORT void br_add_template_list_to_gallery(br_gallery gallery, br_template_list tl); | |
| 568 | 573 | /*! |
| 569 | 574 | * \brief Close the br::Gallery. |
| 570 | 575 | */ | ... | ... |
openbr/openbr_plugin.cpp
| ... | ... | @@ -408,7 +408,7 @@ TemplateList TemplateList::fromGallery(const br::File &gallery) |
| 408 | 408 | QStringList labels; |
| 409 | 409 | for (int i=newTemplates.size()-1; i>=0; i--) { |
| 410 | 410 | newTemplates[i].file.set("Index", i+templates.size()); |
| 411 | - newTemplates[i].file.set("Gallery", gallery.name); | |
| 411 | + newTemplates[i].file.set("Gallery", file.name); | |
| 412 | 412 | |
| 413 | 413 | QString label = newTemplates.at(i).file.get<QString>("Label"); |
| 414 | 414 | // Have we seen this subject before? |
| ... | ... | @@ -436,7 +436,7 @@ TemplateList TemplateList::fromGallery(const br::File &gallery) |
| 436 | 436 | } else { |
| 437 | 437 | for (int i=newTemplates.size()-1; i>=0; i--) { |
| 438 | 438 | newTemplates[i].file.set("Index", i+templates.size()); |
| 439 | - newTemplates[i].file.set("Gallery", gallery.name); | |
| 439 | + newTemplates[i].file.set("Gallery", file.name); | |
| 440 | 440 | |
| 441 | 441 | if (crossValidate > 0) { |
| 442 | 442 | if (newTemplates[i].file.getBool("duplicatePartitions")) { |
| ... | ... | @@ -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. */ |
| ... | ... | @@ -1324,6 +1331,10 @@ protected: |
| 1324 | 1331 | |
| 1325 | 1332 | private: |
| 1326 | 1333 | virtual void compareBlock(const TemplateList &target, const TemplateList &query, Output *output, int targetOffset, int queryOffset) const; |
| 1334 | + | |
| 1335 | + friend struct AlgorithmCore; | |
| 1336 | + virtual bool compare(const File &targetGallery, const File &queryGallery, const File &output) const /*!< \brief Escape hatch for algorithms that need customized file I/O during comparison. */ | |
| 1337 | + { (void) targetGallery; (void) queryGallery; (void) output; return false; } | |
| 1327 | 1338 | }; |
| 1328 | 1339 | |
| 1329 | 1340 | /*! | ... | ... |
openbr/plugins/algorithms.cpp
| ... | ... | @@ -64,6 +64,7 @@ class AlgorithmsInitializer : public Initializer |
| 64 | 64 | Globals->abbreviations.insert("SmallSIFT", "Open+LimitSize(512)+KeyPointDetector(SIFT)+KeyPointDescriptor(SIFT):KeyPointMatcher(BruteForce)"); |
| 65 | 65 | Globals->abbreviations.insert("SmallSURF", "Open+LimitSize(512)+KeyPointDetector(SURF)+KeyPointDescriptor(SURF):KeyPointMatcher(BruteForce)"); |
| 66 | 66 | Globals->abbreviations.insert("ColorHist", "Open+LimitSize(512)+Expand+EnsureChannels(3)+SplitChannels+Hist(256,0,8)+Cat+Normalize(L1):L2"); |
| 67 | + Globals->abbreviations.insert("ImageSimilarity", "Open+EnsureChannels(3)+Resize(256,256)+SplitChannels+RectRegions(64,64,64,64)+Hist(256,0,8)+Cat:NegativeLogPlusOne(L2)"); | |
| 67 | 68 | Globals->abbreviations.insert("ImageClassification", "Open+CropSquare+LimitSize(256)+Cvt(Gray)+Gradient+Bin(0,360,9,true)+Merge+Integral+RecursiveIntegralSampler(4,2,8,Singleton(KMeans(256)))+Cat+CvtFloat+Hist(256)+KNN(5,Dist(L1),false,5)+Rename(KNN,Subject)"); |
| 68 | 69 | Globals->abbreviations.insert("TanTriggs", "Blur(1.1)+Gamma(0.2)+DoG(1,2)+ContrastEq(0.1,10)"); |
| 69 | 70 | ... | ... |
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/independent.cpp
| ... | ... | @@ -9,12 +9,13 @@ using namespace cv; |
| 9 | 9 | namespace br |
| 10 | 10 | { |
| 11 | 11 | |
| 12 | -static TemplateList Downsample(const TemplateList &templates, int classes, int instances, float fraction, const QString & inputVariable) | |
| 12 | +static TemplateList Downsample(const TemplateList &templates, int classes, int instances, float fraction, const QString & inputVariable, const QStringList &gallery) | |
| 13 | 13 | { |
| 14 | 14 | // Return early when no downsampling is required |
| 15 | 15 | if ((classes == std::numeric_limits<int>::max()) && |
| 16 | 16 | (instances == std::numeric_limits<int>::max()) && |
| 17 | - (fraction >= 1)) | |
| 17 | + (fraction >= 1) && | |
| 18 | + (gallery.isEmpty())) | |
| 18 | 19 | return templates; |
| 19 | 20 | |
| 20 | 21 | const bool atLeast = instances < 0; |
| ... | ... | @@ -60,6 +61,11 @@ static TemplateList Downsample(const TemplateList &templates, int classes, int i |
| 60 | 61 | downsample = downsample.mid(0, downsample.size()*fraction); |
| 61 | 62 | } |
| 62 | 63 | |
| 64 | + if (!gallery.isEmpty()) | |
| 65 | + for (int i=downsample.size()-1; i>=0; i--) | |
| 66 | + if (!gallery.contains(downsample[i].file.get<QString>("Gallery"))) | |
| 67 | + downsample.removeAt(i); | |
| 68 | + | |
| 63 | 69 | return downsample; |
| 64 | 70 | } |
| 65 | 71 | |
| ... | ... | @@ -71,11 +77,13 @@ class DownsampleTrainingTransform : public Transform |
| 71 | 77 | Q_PROPERTY(int instances READ get_instances WRITE set_instances RESET reset_instances STORED false) |
| 72 | 78 | Q_PROPERTY(float fraction READ get_fraction WRITE set_fraction RESET reset_fraction STORED false) |
| 73 | 79 | Q_PROPERTY(QString inputVariable READ get_inputVariable WRITE set_inputVariable RESET reset_inputVariable STORED false) |
| 80 | + Q_PROPERTY(QStringList gallery READ get_gallery WRITE set_gallery RESET reset_gallery STORED false) | |
| 74 | 81 | BR_PROPERTY(br::Transform*, transform, NULL) |
| 75 | 82 | BR_PROPERTY(int, classes, std::numeric_limits<int>::max()) |
| 76 | 83 | BR_PROPERTY(int, instances, std::numeric_limits<int>::max()) |
| 77 | 84 | BR_PROPERTY(float, fraction, 1) |
| 78 | 85 | BR_PROPERTY(QString, inputVariable, "Label") |
| 86 | + BR_PROPERTY(QStringList, gallery, QStringList()) | |
| 79 | 87 | |
| 80 | 88 | void project(const Template & src, Template & dst) const |
| 81 | 89 | { |
| ... | ... | @@ -88,7 +96,8 @@ class DownsampleTrainingTransform : public Transform |
| 88 | 96 | if (!transform || !transform->trainable) |
| 89 | 97 | return; |
| 90 | 98 | |
| 91 | - TemplateList downsampled = Downsample(data, classes, instances, fraction, inputVariable); | |
| 99 | + TemplateList downsampled = Downsample(data, classes, instances, fraction, inputVariable, gallery); | |
| 100 | + | |
| 92 | 101 | transform->train(downsampled); |
| 93 | 102 | } |
| 94 | 103 | }; | ... | ... |
openbr/plugins/likely.cmake
0 โ 100644
openbr/plugins/likely.cpp
0 โ 100644
| 1 | +#include <likely.h> | |
| 2 | +#include <likely/opencv.hpp> | |
| 3 | + | |
| 4 | +#include "openbr_internal.h" | |
| 5 | + | |
| 6 | +namespace br | |
| 7 | +{ | |
| 8 | + | |
| 9 | +/*! | |
| 10 | + * \ingroup transforms | |
| 11 | + * \brief Generic interface to Likely JIT compiler | |
| 12 | + * | |
| 13 | + * www.liblikely.org | |
| 14 | + * \author Josh Klontz \cite jklontz | |
| 15 | + */ | |
| 16 | +class LikelyTransform : public UntrainableTransform | |
| 17 | +{ | |
| 18 | + Q_OBJECT | |
| 19 | + Q_PROPERTY(QString kernel READ get_kernel WRITE set_kernel RESET reset_kernel STORED false) | |
| 20 | + BR_PROPERTY(QString, kernel, "") | |
| 21 | + | |
| 22 | + likely_function function; | |
| 23 | + | |
| 24 | + void init() | |
| 25 | + { | |
| 26 | + likely_ast ast = likely_ast_from_string(qPrintable(kernel)); | |
| 27 | + likely_env env = likely_new_env(); | |
| 28 | + function = likely_compile(ast, env, likely_type_null); | |
| 29 | + likely_release_env(env); | |
| 30 | + likely_release_ast(ast); | |
| 31 | + } | |
| 32 | + | |
| 33 | + void project(const Template &src, Template &dst) const | |
| 34 | + { | |
| 35 | + likely_const_mat srcl = likely::fromCvMat(src); | |
| 36 | + likely_const_mat dstl = function(srcl); | |
| 37 | + dst = likely::toCvMat(dstl); | |
| 38 | + likely_release(dstl); | |
| 39 | + likely_release(srcl); | |
| 40 | + } | |
| 41 | +}; | |
| 42 | + | |
| 43 | +BR_REGISTER(Transform, LikelyTransform) | |
| 44 | + | |
| 45 | +/*! | |
| 46 | + * \ingroup formats | |
| 47 | + * \brief Likely matrix format | |
| 48 | + * | |
| 49 | + * www.liblikely.org | |
| 50 | + * \author Josh Klontz \cite jklontz | |
| 51 | + */ | |
| 52 | +class lmFormat : public Format | |
| 53 | +{ | |
| 54 | + Q_OBJECT | |
| 55 | + | |
| 56 | + Template read() const | |
| 57 | + { | |
| 58 | + likely_const_mat m = likely_read(qPrintable(file.name)); | |
| 59 | + Template result(likely::toCvMat(m)); | |
| 60 | + likely_release(m); | |
| 61 | + return result; | |
| 62 | + } | |
| 63 | + | |
| 64 | + void write(const Template &t) const | |
| 65 | + { | |
| 66 | + likely_const_mat m = likely::fromCvMat(t); | |
| 67 | + likely_write(m, qPrintable(file.name)); | |
| 68 | + likely_release(m); | |
| 69 | + } | |
| 70 | +}; | |
| 71 | + | |
| 72 | +BR_REGISTER(Format, lmFormat) | |
| 73 | + | |
| 74 | +} // namespace br | |
| 75 | + | |
| 76 | +#include "likely.moc" | ... | ... |
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/pp5.cpp
| ... | ... | @@ -306,6 +306,22 @@ class PP5CompareDistance : public Distance |
| 306 | 306 | { |
| 307 | 307 | Q_OBJECT |
| 308 | 308 | |
| 309 | + struct NativeGallery | |
| 310 | + { | |
| 311 | + FileList files; | |
| 312 | + QList<int> faceIDs; | |
| 313 | + ppr_gallery_type gallery; | |
| 314 | + }; | |
| 315 | + | |
| 316 | + mutable QMap<QString, NativeGallery> cache; | |
| 317 | + mutable QMutex cacheLock; | |
| 318 | + | |
| 319 | + ~PP5CompareDistance() | |
| 320 | + { | |
| 321 | + foreach (const NativeGallery &gallery, cache.values()) | |
| 322 | + ppr_free_gallery(gallery.gallery); | |
| 323 | + } | |
| 324 | + | |
| 309 | 325 | float compare(const Template &target, const Template &query) const |
| 310 | 326 | { |
| 311 | 327 | TemplateList targetList; |
| ... | ... | @@ -315,7 +331,6 @@ class PP5CompareDistance : public Distance |
| 315 | 331 | MatrixOutput *score = MatrixOutput::make(targetList.files(), queryList.files()); |
| 316 | 332 | compare(targetList, queryList, score); |
| 317 | 333 | return score->data.at<float>(0); |
| 318 | - | |
| 319 | 334 | } |
| 320 | 335 | |
| 321 | 336 | void compare(const TemplateList &target, const TemplateList &query, Output *output) const |
| ... | ... | @@ -326,25 +341,27 @@ class PP5CompareDistance : public Distance |
| 326 | 341 | QList<int> target_face_ids, query_face_ids; |
| 327 | 342 | enroll(target, &target_gallery, target_face_ids); |
| 328 | 343 | enroll(query, &query_gallery, query_face_ids); |
| 344 | + compareNative(target_gallery, target_face_ids, query_gallery, query_face_ids, output); | |
| 345 | + ppr_free_gallery(target_gallery); | |
| 346 | + ppr_free_gallery(query_gallery); | |
| 347 | + } | |
| 329 | 348 | |
| 330 | - ppr_similarity_matrix_type similarity_matrix; | |
| 331 | - TRY(ppr_compare_galleries(context, query_gallery, target_gallery, &similarity_matrix)) | |
| 332 | - | |
| 333 | - for (int i=0; i<query_face_ids.size(); i++) { | |
| 334 | - int query_face_id = query_face_ids[i]; | |
| 335 | - for (int j=0; j<target_face_ids.size(); j++) { | |
| 336 | - int target_face_id = target_face_ids[j]; | |
| 349 | + void compareNative(ppr_gallery_type target, const QList<int> &targetIDs, ppr_gallery_type query, const QList<int> &queryIDs, Output *output) const | |
| 350 | + { | |
| 351 | + ppr_similarity_matrix_type simmat; | |
| 352 | + TRY(ppr_compare_galleries(context, query, target, &simmat)) | |
| 353 | + for (int i=0; i<queryIDs.size(); i++) { | |
| 354 | + int query_face_id = queryIDs[i]; | |
| 355 | + for (int j=0; j<targetIDs.size(); j++) { | |
| 356 | + int target_face_id = targetIDs[j]; | |
| 337 | 357 | float score = -std::numeric_limits<float>::max(); |
| 338 | 358 | if ((query_face_id != -1) && (target_face_id != -1)) { |
| 339 | - TRY(ppr_get_face_similarity_score(context, similarity_matrix, query_face_id, target_face_id, &score)) | |
| 359 | + TRY(ppr_get_face_similarity_score(context, simmat, query_face_id, target_face_id, &score)) | |
| 340 | 360 | } |
| 341 | 361 | output->setRelative(score, i, j); |
| 342 | 362 | } |
| 343 | 363 | } |
| 344 | - | |
| 345 | - ppr_free_similarity_matrix(similarity_matrix); | |
| 346 | - ppr_free_gallery(target_gallery); | |
| 347 | - ppr_free_gallery(query_gallery); | |
| 364 | + ppr_free_similarity_matrix(simmat); | |
| 348 | 365 | } |
| 349 | 366 | |
| 350 | 367 | void enroll(const TemplateList &templates, ppr_gallery_type *gallery, QList<int> &face_ids) const |
| ... | ... | @@ -363,6 +380,53 @@ class PP5CompareDistance : public Distance |
| 363 | 380 | } |
| 364 | 381 | } |
| 365 | 382 | } |
| 383 | + | |
| 384 | + NativeGallery cacheRetain(const File &gallery) const | |
| 385 | + { | |
| 386 | + QMutexLocker locker(&cacheLock); | |
| 387 | + NativeGallery nativeGallery; | |
| 388 | + if (cache.contains(gallery.name)) { | |
| 389 | + nativeGallery = cache[gallery.name]; | |
| 390 | + } else { | |
| 391 | + ppr_create_gallery(context, &nativeGallery.gallery); | |
| 392 | + TemplateList templates = TemplateList::fromGallery(gallery); | |
| 393 | + enroll(templates, &nativeGallery.gallery, nativeGallery.faceIDs); | |
| 394 | + nativeGallery.files = templates.files(); | |
| 395 | + if (gallery.get<bool>("retain")) | |
| 396 | + cache.insert(gallery.name, nativeGallery); | |
| 397 | + } | |
| 398 | + return nativeGallery; | |
| 399 | + } | |
| 400 | + | |
| 401 | + void cacheRelease(const File &gallery, const NativeGallery &nativeGallery) const | |
| 402 | + { | |
| 403 | + QMutexLocker locker(&cacheLock); | |
| 404 | + if (cache.contains(gallery.name)) { | |
| 405 | + if (gallery.get<bool>("release")) { | |
| 406 | + cache.remove(gallery.name); | |
| 407 | + ppr_free_gallery(nativeGallery.gallery); | |
| 408 | + } | |
| 409 | + } else { | |
| 410 | + ppr_free_gallery(nativeGallery.gallery); | |
| 411 | + } | |
| 412 | + } | |
| 413 | + | |
| 414 | + bool compare(const File &targetGallery, const File &queryGallery, const File &output) const | |
| 415 | + { | |
| 416 | + if (!targetGallery.get<bool>("native") || !queryGallery.get<bool>("native")) | |
| 417 | + return false; | |
| 418 | + | |
| 419 | + NativeGallery nativeTarget = cacheRetain(targetGallery); | |
| 420 | + NativeGallery nativeQuery = cacheRetain(queryGallery); | |
| 421 | + | |
| 422 | + QScopedPointer<Output> o(Output::make(output, nativeTarget.files, nativeQuery.files)); | |
| 423 | + o->setBlock(0, 0); | |
| 424 | + compareNative(nativeTarget.gallery, nativeTarget.faceIDs, nativeQuery.gallery, nativeQuery.faceIDs, o.data()); | |
| 425 | + | |
| 426 | + cacheRelease(targetGallery, nativeTarget); | |
| 427 | + cacheRelease(queryGallery, nativeQuery); | |
| 428 | + return true; | |
| 429 | + } | |
| 366 | 430 | }; |
| 367 | 431 | |
| 368 | 432 | BR_REGISTER(Distance, PP5CompareDistance) | ... | ... |
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) { | ... | ... |
scripts/brpy/__init__.py
| ... | ... | @@ -21,13 +21,27 @@ def _handle_string_func(func): |
| 21 | 21 | return call_func |
| 22 | 22 | |
| 23 | 23 | def init_brpy(br_loc='/usr/local/lib'): |
| 24 | - """Takes the ctypes lib object for br and initializes all function inputs and outputs""" | |
| 25 | - br_loc += '/libopenbr.%s' | |
| 26 | - if os.path.exists(br_loc % 'dylib'): | |
| 27 | - br = cdll.LoadLibrary(br_loc % 'dylib') | |
| 28 | - elif os.path.exists(br_loc % 'so'): | |
| 29 | - br = cdll.LoadLibrary(br_loc % 'so') | |
| 30 | - else: | |
| 24 | + """Initializes all function inputs and outputs for the br ctypes lib object""" | |
| 25 | + | |
| 26 | + lib_path = os.environ.get('LD_LIBRARY_PATH') | |
| 27 | + paths = [br_loc] | |
| 28 | + if lib_path: | |
| 29 | + paths.extend(lib_path.split(':')) | |
| 30 | + | |
| 31 | + found = False | |
| 32 | + for p in paths: | |
| 33 | + dylib = '%s/%s.%s' % (p, 'libopenbr', 'dylib') | |
| 34 | + so = '%s/%s.%s' % (p, 'libopenbr', 'so') | |
| 35 | + if os.path.exists(dylib): | |
| 36 | + br = cdll.LoadLibrary(dylib) | |
| 37 | + found = True | |
| 38 | + break | |
| 39 | + elif os.path.exists(so): | |
| 40 | + br = cdll.LoadLibrary(so) | |
| 41 | + found = True | |
| 42 | + break | |
| 43 | + | |
| 44 | + if not found: | |
| 31 | 45 | raise ValueError('Neither .so nor .dylib libopenbr found in %s' % br_loc) |
| 32 | 46 | |
| 33 | 47 | plot_args = _var_string_args(1) + [c_bool] |
| ... | ... | @@ -44,7 +58,7 @@ def init_brpy(br_loc='/usr/local/lib'): |
| 44 | 58 | br.br_eval.argtypes = _string_args(3) |
| 45 | 59 | br.br_eval.restype = c_float |
| 46 | 60 | br.br_eval_classification.argtypes = _string_args(4) |
| 47 | - br.br_eval_clustering.argtypes = _string_args(2) | |
| 61 | + br.br_eval_clustering.argtypes = _string_args(3) | |
| 48 | 62 | br.br_eval_detection.argtypes = _string_args(3) |
| 49 | 63 | br.br_eval_detection.restype = c_float |
| 50 | 64 | br.br_eval_landmarking.argtypes = _string_args(3) + [c_int, c_int] |
| ... | ... | @@ -129,7 +143,8 @@ def init_brpy(br_loc='/usr/local/lib'): |
| 129 | 143 | br.br_make_gallery.restype = c_void_p |
| 130 | 144 | br.br_load_from_gallery.argtypes = [c_void_p] |
| 131 | 145 | br.br_load_from_gallery.restype = c_void_p |
| 132 | - br.br_add_to_gallery.argtypes = [c_void_p, c_void_p] | |
| 146 | + br.br_add_template_to_gallery.argtypes = [c_void_p, c_void_p] | |
| 147 | + br.br_add_template_list_to_gallery.argtypes = [c_void_p, c_void_p] | |
| 133 | 148 | br.br_close_gallery.argtypes = [c_void_p] |
| 134 | 149 | |
| 135 | 150 | return br | ... | ... |