diff --git a/3rdparty/stasm4.0.0/stasm/asm.cpp b/3rdparty/stasm4.0.0/stasm/asm.cpp index ee300f1..e7a36cb 100755 --- a/3rdparty/stasm4.0.0/stasm/asm.cpp +++ b/3rdparty/stasm4.0.0/stasm/asm.cpp @@ -5,8 +5,6 @@ #include "stasm.h" #include "stasmhash.h" -#include - namespace stasm { static void TraceShape( // write an image file showing current shape on the image diff --git a/3rdparty/stasm4.0.0/stasm/faceroi.cpp b/3rdparty/stasm4.0.0/stasm/faceroi.cpp index e999cfd..b7f9120 100755 --- a/3rdparty/stasm4.0.0/stasm/faceroi.cpp +++ b/3rdparty/stasm4.0.0/stasm/faceroi.cpp @@ -3,7 +3,6 @@ // Copyright (C) 2005-2013, Stephen Milborrow #include "stasm.h" -#include namespace stasm { diff --git a/3rdparty/stasm4.0.0/stasm/pinstart.cpp b/3rdparty/stasm4.0.0/stasm/pinstart.cpp index 55c14ec..e47be40 100755 --- a/3rdparty/stasm4.0.0/stasm/pinstart.cpp +++ b/3rdparty/stasm4.0.0/stasm/pinstart.cpp @@ -3,7 +3,6 @@ // Copyright (C) 2005-2013, Stephen Milborrow #include "stasm.h" -#include namespace stasm { diff --git a/3rdparty/stasm4.0.0/stasm/stasm_lib.cpp b/3rdparty/stasm4.0.0/stasm/stasm_lib.cpp index 6295fa8..bfea3f9 100755 --- a/3rdparty/stasm4.0.0/stasm/stasm_lib.cpp +++ b/3rdparty/stasm4.0.0/stasm/stasm_lib.cpp @@ -3,7 +3,6 @@ // Copyright (C) 2005-2013, Stephen Milborrow #include "stasm.h" -#include using namespace stasm; diff --git a/app/br/br.cpp b/app/br/br.cpp index e78c71d..49f9a95 100644 --- a/app/br/br.cpp +++ b/app/br/br.cpp @@ -138,8 +138,8 @@ public: check(parc >= 2 && parc <= 4, "Incorrect parameter count for 'evalClassification'."); br_eval_classification(parv[0], parv[1], parc >= 3 ? parv[2] : "", parc >= 4 ? parv[3] : ""); } else if (!strcmp(fun, "evalClustering")) { - check(parc == 2, "Incorrect parameter count for 'evalClustering'."); - br_eval_clustering(parv[0], parv[1]); + check((parc >= 2) && (parc <= 3), "Incorrect parameter count for 'evalClustering'."); + br_eval_clustering(parv[0], parv[1], parc == 3 ? parv[2] : ""); } else if (!strcmp(fun, "evalDetection")) { check((parc >= 2) && (parc <= 3), "Incorrect parameter count for 'evalDetection'."); br_eval_detection(parv[0], parv[1], parc == 3 ? parv[2] : ""); diff --git a/openbr/core/cluster.cpp b/openbr/core/cluster.cpp index 9b3beda..9e87b44 100644 --- a/openbr/core/cluster.cpp +++ b/openbr/core/cluster.cpp @@ -100,7 +100,9 @@ Neighborhood getNeighborhood(const QStringList &simmats) int currentRows = -1; int columnOffset = 0; for (int j=0; j format(br::Factory::make(simmats[i*numGalleries+j])); + br::Template t = format->read(); + cv::Mat m = t.m(); if (j==0) { currentRows = m.rows; allNeighbors.resize(currentRows); @@ -115,8 +117,9 @@ Neighborhood getNeighborhood(const QStringList &simmats) float val = m.at(k,l); if ((i==j) && (k==l)) continue; // Skips self-similarity scores - if ((val != -std::numeric_limits::infinity()) && - (val != std::numeric_limits::infinity())) { + if (val != -std::numeric_limits::max() + && val != -std::numeric_limits::infinity() + && val != std::numeric_limits::infinity()) { globalMax = std::max(globalMax, val); globalMin = std::min(globalMin, val); } @@ -157,7 +160,7 @@ Neighborhood getNeighborhood(const QStringList &simmats) // Zhu et al. "A Rank-Order Distance based Clustering Algorithm for Face Tagging", CVPR 2011 br::Clusters br::ClusterGallery(const QStringList &simmats, float aggressiveness, const QString &csv) { - qDebug("Clustering %d simmat(s)", simmats.size()); + qDebug("Clustering %d simmat(s), aggressiveness %f", simmats.size(), aggressiveness); // Read in gallery parts, keeping top neighbors of each template Neighborhood neighborhood = getNeighborhood(simmats); @@ -275,13 +278,14 @@ float jaccardIndex(const QVector &indicesA, const QVector &indicesB) // Evaluates clustering algorithms based on metrics described in // Santo Fortunato "Community detection in graphs", Physics Reports 486 (2010) -void br::EvalClustering(const QString &csv, const QString &input) +void br::EvalClustering(const QString &csv, const QString &input, QString truth_property) { + if (truth_property.isEmpty()) + truth_property = "Label"; qDebug("Evaluating %s against %s", qPrintable(csv), qPrintable(input)); - // We assume clustering algorithms store assigned cluster labels as integers (since the clusters are - // not named). Direct use of ClusterID is not general -cao - QList labels = File::get(TemplateList::fromGallery(input), "ClusterID"); + TemplateList tList = TemplateList::fromGallery(input); + QList labels = tList.indexProperty(truth_property); QHash labelToIndex; int nClusters = 0; diff --git a/openbr/core/cluster.h b/openbr/core/cluster.h index 9d5ce7b..84bb9ba 100644 --- a/openbr/core/cluster.h +++ b/openbr/core/cluster.h @@ -28,7 +28,7 @@ namespace br typedef QVector Clusters; Clusters ClusterGallery(const QStringList &simmats, float aggressiveness, const QString &csv); - void EvalClustering(const QString &csv, const QString &input); + void EvalClustering(const QString &csv, const QString &input, QString truth_property); Clusters ReadClusters(const QString &csv); void WriteClusters(const Clusters &clusters, const QString &csv); diff --git a/openbr/core/core.cpp b/openbr/core/core.cpp index 962d5b6..6260193 100644 --- a/openbr/core/core.cpp +++ b/openbr/core/core.cpp @@ -21,14 +21,16 @@ #include "qtutils.h" #include "../plugins/openbr_internal.h" -using namespace br; +namespace br { -/**** ALGORITHM_CORE ****/ struct AlgorithmCore { QSharedPointer transform; QSharedPointer distance; + QString transformString; + QString distanceString; + AlgorithmCore(const QString &name) { this->name = name; @@ -134,6 +136,8 @@ struct AlgorithmCore } TemplateList data(TemplateList::fromGallery(input)); + bool multiProcess = Globals->file.getBool("multiProcess", false); + if (gallery.contains("append")) { // Remove any templates which are already in the gallery @@ -155,20 +159,27 @@ struct AlgorithmCore Globals->currentStep = 0; Globals->totalSteps = data.length(); - // Trust me, this makes complete sense. - // We're just going to make a pipe with a placeholder first transform - QString pipeDesc = "Identity+GalleryOutput("+gallery.flat()+")+ProgressCounter("+QString::number(data.length())+")+Discard"; - QScopedPointer basePipe(Transform::make(pipeDesc,NULL)); + QScopedPointer basePipe; - CompositeTransform * downcast = dynamic_cast(basePipe.data()); - if (downcast == NULL) - qFatal("downcast failed?"); + if (!multiProcess) + { + QString pipeDesc = "Identity+GalleryOutput("+gallery.flat()+")+ProgressCounter("+QString::number(data.length())+")+Discard"; + basePipe.reset(Transform::make(pipeDesc,NULL)); + CompositeTransform * downcast = dynamic_cast(basePipe.data()); + if (downcast == NULL) + qFatal("downcast failed?"); - // replace that placeholder with the current algorithm - downcast->transforms[0] = this->transform.data(); + // replace that placeholder with the current algorithm + downcast->transforms[0] = this->transform.data(); - // call init on the pipe to collapse the algorithm (if its top level is a pipe) - downcast->init(); + // call init on the pipe to collapse the algorithm (if its top level is a pipe) + downcast->init(); + } + else + { + QString pipeDesc = "ProcessWrapper("+transformString+")"+"+GalleryOutput("+gallery.flat()+")+ProgressCounter("+QString::number(data.length())+")+Discard"; + basePipe.reset(Transform::make(pipeDesc,NULL)); + } // Next, we make a Stream (with placeholder transform) QString streamDesc = "Stream(Identity, readMode=DistributeFrames)"; @@ -176,7 +187,7 @@ struct AlgorithmCore WrapperTransform * wrapper = dynamic_cast (baseStream.data()); // replace that placeholder with the pipe we built - wrapper->transform = downcast; + wrapper->transform = basePipe.data(); // and get the final stream's stages by reinterpreting the pipe. Perfectly straightforward. wrapper->init(); @@ -241,17 +252,14 @@ struct AlgorithmCore dummyTarget.append(targets[0]); QScopedPointer realOutput(Output::make(output, dummyTarget, queryFiles)); - // Some outputs assume Globals->blockSize is a real thing, of course we have no interest in it. - int old_block_size = Globals->blockSize; - Globals->blockSize = INT_MAX; + realOutput->set_blockRows(INT_MAX); + realOutput->set_blockCols(INT_MAX); realOutput->setBlock(0,0); for (int i=0; i < queries.length(); i++) { float res = distance->compare(queries[i], targets[i]); realOutput->setRelative(res, 0,i); } - - Globals->blockSize = old_block_size; } void deduplicate(const File &inputGallery, const File &outputGallery, const float threshold) @@ -310,7 +318,11 @@ struct AlgorithmCore qPrintable(queryGallery.flat()), output.isNull() ? "" : qPrintable(" to " + output.flat())); - if (output.exists() && output.get("cache", false)) return; + // Escape hatch for distances that need to operate directly on the gallery files + if (distance->compare(targetGallery, queryGallery, output)) + return; + + if (output.exists() && output.get("cache")) return; if (queryGallery == ".") queryGallery = targetGallery; QScopedPointer t, q; @@ -327,11 +339,13 @@ struct AlgorithmCore File splitOutputFile = output.name.arg(i); outputFiles.append(splitOutputFile); } + } else { + outputFiles.append(output); } - else outputFiles.append(output); QList outputs; - foreach (const File &outputFile, outputFiles) outputs.append(Output::make(outputFile, targetFiles, queryFiles)); + foreach (const File &outputFile, outputFiles) + outputs.append(Output::make(outputFile, targetFiles, queryFiles)); if (distance.isNull()) qFatal("Null distance."); Globals->currentStep = 0; @@ -361,7 +375,6 @@ struct AlgorithmCore else targetPartitions.append(targets); outputs[i]->setBlock(queryBlock, targetBlock); - distance->compare(targetPartitions[i], queryPartitions[i], outputs[i]); Globals->currentStep += double(targets.size()) * double(queries.size()); @@ -407,14 +420,22 @@ private: if ((words.size() < 1) || (words.size() > 2)) qFatal("Invalid algorithm format."); //! [Parsing the algorithm description] + transformString = words[0]; + //! [Creating the template generation and comparison methods] transform = QSharedPointer(Transform::make(words[0], NULL)); - if (words.size() > 1) distance = QSharedPointer(Distance::make(words[1], NULL)); + if (words.size() > 1) { + distance = QSharedPointer(Distance::make(words[1], NULL)); + distanceString = words[1]; + } //! [Creating the template generation and comparison methods] } }; +} // namespace br + +using namespace br; class AlgorithmManager : public Initializer { diff --git a/openbr/core/plot.cpp b/openbr/core/plot.cpp index 1206e7d..f6e9802 100644 --- a/openbr/core/plot.cpp +++ b/openbr/core/plot.cpp @@ -247,7 +247,7 @@ bool Plot(const QStringList &files, const File &destination, bool show) (p.major.size > 1 ? getScale("colour", p.major.header, p.major.size) : QString()) + (p.minor.size > 1 ? QString(" + scale_linetype_discrete(\"%1\")").arg(p.minor.header) : QString()) + QString(" + scale_x_log10(labels=trans_format(\"log10\", math_format()))") + - (rocOpts.contains("yLimits") ? QString(" + scale_y_continuous(labels=percent, limits=%3)").arg("c"+QtUtils::toString(rocOpts.get("yLimits",QPointF()))) : QString(" + scale_y_continuous(labels=percent)")) + + (rocOpts.contains("yLimits") ? QString(" + scale_y_continuous(labels=percent) + coord_cartesian(ylim=%1)").arg("c"+QtUtils::toString(rocOpts.get("yLimits",QPointF()))) : QString(" + scale_y_continuous(labels=percent)")) + QString(" + annotation_logticks(sides=\"b\")") + 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)," " 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("textSize",12)), rocOpts.contains("legendPosition") ? "c"+QtUtils::toString(rocOpts.get("legendPosition")) : "'right'"))); @@ -273,7 +273,7 @@ bool Plot(const QStringList &files, const File &destination, bool show) (minimalist ? "" : " + scale_x_log10(labels=c(1,5,10,50,100), breaks=c(1,5,10,50,100)) + annotation_logticks(sides=\"b\")") + (p.major.size > 1 ? getScale("colour", p.major.header, p.major.size) : QString()) + (p.minor.size > 1 ? QString(" + scale_linetype_discrete(\"%1\")").arg(p.minor.header) : QString()) + - (cmcOpts.contains("yLimits") ? QString(" + scale_y_continuous(labels=percent, limits=%3)").arg("c"+QtUtils::toString(cmcOpts.get("yLimits",QPointF()))) : QString(" + scale_y_continuous(labels=percent)")) + + (cmcOpts.contains("yLimits") ? QString(" + scale_y_continuous(labels=percent) + coord_cartesian(ylim=%1)").arg("c"+QtUtils::toString(cmcOpts.get("yLimits",QPointF()))) : QString(" + scale_y_continuous(labels=percent)")) + 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)," " 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("textSize",12)), cmcOpts.contains("legendPosition") ? "c"+QtUtils::toString(cmcOpts.get("legendPosition")) : "'right'"))); diff --git a/openbr/openbr.cpp b/openbr/openbr.cpp index e1d67fe..3e9d45b 100644 --- a/openbr/openbr.cpp +++ b/openbr/openbr.cpp @@ -109,9 +109,9 @@ void br_eval_classification(const char *predicted_gallery, const char *truth_gal EvalClassification(predicted_gallery, truth_gallery, predicted_property, truth_property); } -void br_eval_clustering(const char *csv, const char *gallery) +void br_eval_clustering(const char *csv, const char *gallery, const char * truth_property) { - EvalClustering(csv, gallery); + EvalClustering(csv, gallery, truth_property); } 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) return (br_template_list)tl; } -void br_add_to_gallery(br_gallery gallery, br_template_list tl) +void br_add_template_to_gallery(br_gallery gallery, br_template tmpl) +{ + Gallery *gal = reinterpret_cast(gallery); + Template *t = reinterpret_cast(tmpl); + gal->write(*t); +} + +void br_add_template_list_to_gallery(br_gallery gallery, br_template_list tl) { Gallery *gal = reinterpret_cast(gallery); TemplateList *realTL = reinterpret_cast(tl); diff --git a/openbr/openbr.h b/openbr/openbr.h index f2cce16..0c3a22f 100644 --- a/openbr/openbr.h +++ b/openbr/openbr.h @@ -167,9 +167,10 @@ BR_EXPORT void br_eval_classification(const char *predicted_gallery, const char * \brief Evaluates and prints clustering accuracy to the terminal. * \param csv The cluster results file. * \param gallery The br::Gallery used to generate the \ref simmat that was clustered. + * \param truth_property (Optional) which metadata key to use from gallery, defaults to Label * \see br_cluster */ -BR_EXPORT void br_eval_clustering(const char *csv, const char *gallery); +BR_EXPORT void br_eval_clustering(const char *csv, const char *gallery, const char * truth_property); /*! * \brief Evaluates and prints detection accuracy to terminal. @@ -562,9 +563,13 @@ BR_EXPORT br_gallery br_make_gallery(const char *gallery); */ BR_EXPORT br_template_list br_load_from_gallery(br_gallery gallery); /*! + * \brief Write a br::Template to the br::Gallery on disk. + */ +BR_EXPORT void br_add_template_to_gallery(br_gallery gallery, br_template tmpl); +/*! * \brief Write a br::TemplateList to the br::Gallery on disk. */ -BR_EXPORT void br_add_to_gallery(br_gallery gallery, br_template_list tl); +BR_EXPORT void br_add_template_list_to_gallery(br_gallery gallery, br_template_list tl); /*! * \brief Close the br::Gallery. */ diff --git a/openbr/openbr_plugin.cpp b/openbr/openbr_plugin.cpp index 1740bde..47791fe 100644 --- a/openbr/openbr_plugin.cpp +++ b/openbr/openbr_plugin.cpp @@ -408,7 +408,7 @@ TemplateList TemplateList::fromGallery(const br::File &gallery) QStringList labels; for (int i=newTemplates.size()-1; i>=0; i--) { newTemplates[i].file.set("Index", i+templates.size()); - newTemplates[i].file.set("Gallery", gallery.name); + newTemplates[i].file.set("Gallery", file.name); QString label = newTemplates.at(i).file.get("Label"); // Have we seen this subject before? @@ -436,7 +436,7 @@ TemplateList TemplateList::fromGallery(const br::File &gallery) } else { for (int i=newTemplates.size()-1; i>=0; i--) { newTemplates[i].file.set("Index", i+templates.size()); - newTemplates[i].file.set("Gallery", gallery.name); + newTemplates[i].file.set("Gallery", file.name); if (crossValidate > 0) { if (newTemplates[i].file.getBool("duplicatePartitions")) { @@ -1101,13 +1101,19 @@ void Output::initialize(const FileList &targetFiles, const FileList &queryFiles) { this->targetFiles = targetFiles; this->queryFiles = queryFiles; + if (this->blockRows == -1) + blockRows = Globals->blockSize; + + if (this->blockCols == -1) + blockCols = Globals->blockSize; + selfSimilar = (queryFiles == targetFiles) && (targetFiles.size() > 1) && (queryFiles.size() > 1); } void Output::setBlock(int rowBlock, int columnBlock) { - offset = QPoint((columnBlock == -1) ? 0 : Globals->blockSize*columnBlock, - (rowBlock == -1) ? 0 : Globals->blockSize*rowBlock); + offset = QPoint((columnBlock == -1) ? 0 : blockCols*columnBlock, + (rowBlock == -1) ? 0 : blockRows*rowBlock); if (!next.isNull()) next->setBlock(rowBlock, columnBlock); } diff --git a/openbr/openbr_plugin.h b/openbr/openbr_plugin.h index 3f84d7e..cd23aba 100644 --- a/openbr/openbr_plugin.h +++ b/openbr/openbr_plugin.h @@ -996,6 +996,11 @@ class BR_EXPORT Output : public Object Q_OBJECT public: + Q_PROPERTY(int blockRows READ get_blockRows WRITE set_blockRows RESET reset_blockRows STORED false) + Q_PROPERTY(int blockCols READ get_blockCols WRITE set_blockCols RESET reset_blockCols STORED false) + BR_PROPERTY(int, blockRows, -1) + BR_PROPERTY(int, blockCols, -1) + FileList targetFiles; /*!< \brief List of files representing the gallery templates. */ FileList queryFiles; /*!< \brief List of files representing the probe templates. */ bool selfSimilar; /*!< \brief \c true if the \em targetFiles == \em queryFiles, \c false otherwise. */ @@ -1074,9 +1079,11 @@ public: */ class BR_EXPORT Gallery : public Object { - Q_OBJECT - + Q_OBJECT public: + Q_PROPERTY(int readBlockSize READ get_readBlockSize WRITE set_readBlockSize RESET reset_readBlockSize STORED false) + BR_PROPERTY(int, readBlockSize, Globals->blockSize) + virtual ~Gallery() {} TemplateList read(); /*!< \brief Retrieve all the stored templates. */ FileList files(); /*!< \brief Retrieve all the stored template files. */ @@ -1324,6 +1331,10 @@ protected: private: virtual void compareBlock(const TemplateList &target, const TemplateList &query, Output *output, int targetOffset, int queryOffset) const; + + friend struct AlgorithmCore; + 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. */ + { (void) targetGallery; (void) queryGallery; (void) output; return false; } }; /*! diff --git a/openbr/plugins/algorithms.cpp b/openbr/plugins/algorithms.cpp index 06e3275..0fe6d65 100644 --- a/openbr/plugins/algorithms.cpp +++ b/openbr/plugins/algorithms.cpp @@ -64,6 +64,7 @@ class AlgorithmsInitializer : public Initializer Globals->abbreviations.insert("SmallSIFT", "Open+LimitSize(512)+KeyPointDetector(SIFT)+KeyPointDescriptor(SIFT):KeyPointMatcher(BruteForce)"); Globals->abbreviations.insert("SmallSURF", "Open+LimitSize(512)+KeyPointDetector(SURF)+KeyPointDescriptor(SURF):KeyPointMatcher(BruteForce)"); Globals->abbreviations.insert("ColorHist", "Open+LimitSize(512)+Expand+EnsureChannels(3)+SplitChannels+Hist(256,0,8)+Cat+Normalize(L1):L2"); + Globals->abbreviations.insert("ImageSimilarity", "Open+EnsureChannels(3)+Resize(256,256)+SplitChannels+RectRegions(64,64,64,64)+Hist(256,0,8)+Cat:NegativeLogPlusOne(L2)"); 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)"); Globals->abbreviations.insert("TanTriggs", "Blur(1.1)+Gamma(0.2)+DoG(1,2)+ContrastEq(0.1,10)"); diff --git a/openbr/plugins/distance.cpp b/openbr/plugins/distance.cpp index b3c9d70..057e218 100644 --- a/openbr/plugins/distance.cpp +++ b/openbr/plugins/distance.cpp @@ -460,12 +460,13 @@ BR_REGISTER(Distance, SumDistance) class GalleryCompareTransform : public Transform { Q_OBJECT - Q_PROPERTY(br::Distance *distance READ get_distance WRITE set_distance RESET reset_distance STORED false) + Q_PROPERTY(QString distanceAlgorithm READ get_distanceAlgorithm WRITE set_distanceAlgorithm RESET reset_distanceAlgorithm STORED false) Q_PROPERTY(QString galleryName READ get_galleryName WRITE set_galleryName RESET reset_galleryName STORED false) - BR_PROPERTY(br::Distance*, distance, NULL) + BR_PROPERTY(QString, distanceAlgorithm, "") BR_PROPERTY(QString, galleryName, "") TemplateList gallery; + QSharedPointer distance; void project(const Template &src, Template &dst) const { @@ -479,8 +480,13 @@ class GalleryCompareTransform : public Transform void init() { - if (!galleryName.isEmpty()) + if (!galleryName.isEmpty()) { gallery = TemplateList::fromGallery(galleryName); + } + if (!distanceAlgorithm.isEmpty()) + { + distance = Distance::fromAlgorithm(distanceAlgorithm); + } } }; diff --git a/openbr/plugins/gallery.cpp b/openbr/plugins/gallery.cpp index fbc4950..a57aae2 100644 --- a/openbr/plugins/gallery.cpp +++ b/openbr/plugins/gallery.cpp @@ -123,7 +123,7 @@ class galGallery : public Gallery gallery.seek(0); TemplateList templates; - while ((templates.size() < Globals->blockSize) && !stream.atEnd()) { + while ((templates.size() < readBlockSize) && !stream.atEnd()) { Template m; stream >> m; templates.append(m); @@ -348,8 +348,8 @@ class memGallery : public Gallery MemoryGalleries::aligned[file] = true; } - TemplateList templates = MemoryGalleries::galleries[file].mid(block*Globals->blockSize, Globals->blockSize); - *done = (templates.size() < Globals->blockSize); + TemplateList templates = MemoryGalleries::galleries[file].mid(block*readBlockSize, readBlockSize); + *done = (templates.size() < readBlockSize); block = *done ? 0 : block+1; return templates; } diff --git a/openbr/plugins/independent.cpp b/openbr/plugins/independent.cpp index 156cc75..7781b74 100644 --- a/openbr/plugins/independent.cpp +++ b/openbr/plugins/independent.cpp @@ -9,12 +9,13 @@ using namespace cv; namespace br { -static TemplateList Downsample(const TemplateList &templates, int classes, int instances, float fraction, const QString & inputVariable) +static TemplateList Downsample(const TemplateList &templates, int classes, int instances, float fraction, const QString & inputVariable, const QStringList &gallery) { // Return early when no downsampling is required if ((classes == std::numeric_limits::max()) && (instances == std::numeric_limits::max()) && - (fraction >= 1)) + (fraction >= 1) && + (gallery.isEmpty())) return templates; const bool atLeast = instances < 0; @@ -60,6 +61,11 @@ static TemplateList Downsample(const TemplateList &templates, int classes, int i downsample = downsample.mid(0, downsample.size()*fraction); } + if (!gallery.isEmpty()) + for (int i=downsample.size()-1; i>=0; i--) + if (!gallery.contains(downsample[i].file.get("Gallery"))) + downsample.removeAt(i); + return downsample; } @@ -71,11 +77,13 @@ class DownsampleTrainingTransform : public Transform Q_PROPERTY(int instances READ get_instances WRITE set_instances RESET reset_instances STORED false) Q_PROPERTY(float fraction READ get_fraction WRITE set_fraction RESET reset_fraction STORED false) Q_PROPERTY(QString inputVariable READ get_inputVariable WRITE set_inputVariable RESET reset_inputVariable STORED false) + Q_PROPERTY(QStringList gallery READ get_gallery WRITE set_gallery RESET reset_gallery STORED false) BR_PROPERTY(br::Transform*, transform, NULL) BR_PROPERTY(int, classes, std::numeric_limits::max()) BR_PROPERTY(int, instances, std::numeric_limits::max()) BR_PROPERTY(float, fraction, 1) BR_PROPERTY(QString, inputVariable, "Label") + BR_PROPERTY(QStringList, gallery, QStringList()) void project(const Template & src, Template & dst) const { @@ -88,7 +96,8 @@ class DownsampleTrainingTransform : public Transform if (!transform || !transform->trainable) return; - TemplateList downsampled = Downsample(data, classes, instances, fraction, inputVariable); + TemplateList downsampled = Downsample(data, classes, instances, fraction, inputVariable, gallery); + transform->train(downsampled); } }; diff --git a/openbr/plugins/likely.cmake b/openbr/plugins/likely.cmake new file mode 100644 index 0000000..92ab602 --- /dev/null +++ b/openbr/plugins/likely.cmake @@ -0,0 +1,7 @@ +set(BR_WITH_LIKELY OFF CACHE BOOL "Build with Likely") + +if(${BR_WITH_LIKELY}) + find_package(Likely REQUIRED) + set(BR_THIRDPARTY_SRC ${BR_THIRDPARTY_SRC} plugins/likely.cpp) + set(BR_THIRDPARTY_LIBS ${BR_THIRDPARTY_LIBS} ${Likely_LIBS}) +endif() diff --git a/openbr/plugins/likely.cpp b/openbr/plugins/likely.cpp new file mode 100644 index 0000000..2ecf0fc --- /dev/null +++ b/openbr/plugins/likely.cpp @@ -0,0 +1,76 @@ +#include +#include + +#include "openbr_internal.h" + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Generic interface to Likely JIT compiler + * + * www.liblikely.org + * \author Josh Klontz \cite jklontz + */ +class LikelyTransform : public UntrainableTransform +{ + Q_OBJECT + Q_PROPERTY(QString kernel READ get_kernel WRITE set_kernel RESET reset_kernel STORED false) + BR_PROPERTY(QString, kernel, "") + + likely_function function; + + void init() + { + likely_ast ast = likely_ast_from_string(qPrintable(kernel)); + likely_env env = likely_new_env(); + function = likely_compile(ast, env, likely_type_null); + likely_release_env(env); + likely_release_ast(ast); + } + + void project(const Template &src, Template &dst) const + { + likely_const_mat srcl = likely::fromCvMat(src); + likely_const_mat dstl = function(srcl); + dst = likely::toCvMat(dstl); + likely_release(dstl); + likely_release(srcl); + } +}; + +BR_REGISTER(Transform, LikelyTransform) + +/*! + * \ingroup formats + * \brief Likely matrix format + * + * www.liblikely.org + * \author Josh Klontz \cite jklontz + */ +class lmFormat : public Format +{ + Q_OBJECT + + Template read() const + { + likely_const_mat m = likely_read(qPrintable(file.name)); + Template result(likely::toCvMat(m)); + likely_release(m); + return result; + } + + void write(const Template &t) const + { + likely_const_mat m = likely::fromCvMat(t); + likely_write(m, qPrintable(file.name)); + likely_release(m); + } +}; + +BR_REGISTER(Format, lmFormat) + +} // namespace br + +#include "likely.moc" diff --git a/openbr/plugins/misc.cpp b/openbr/plugins/misc.cpp index 16c8c39..eee78ca 100644 --- a/openbr/plugins/misc.cpp +++ b/openbr/plugins/misc.cpp @@ -552,6 +552,146 @@ public: BR_REGISTER(Transform, ProgressCounterTransform) + +class OutputTransform : public TimeVaryingTransform +{ + Q_OBJECT + + Q_PROPERTY(QString outputString READ get_outputString WRITE set_outputString RESET reset_outputString STORED false) + // names of mem galleries containing filelists we need. + Q_PROPERTY(QString targetName READ get_targetName WRITE set_targetName RESET reset_targetName STORED false) + Q_PROPERTY(QString queryName READ get_queryName WRITE set_queryName RESET reset_queryName STORED false) + Q_PROPERTY(bool transposeMode READ get_transposeMode WRITE set_transposeMode RESET reset_transposeMode STORED false) + + BR_PROPERTY(QString, outputString, "") + BR_PROPERTY(QString, targetName, "") + BR_PROPERTY(QString, queryName, "") + + BR_PROPERTY(bool,transposeMode, false) + ; + + void projectUpdate(const TemplateList &src, TemplateList &dst) + { + dst = src; + + if (src.empty()) + return; + + // we received a template, which is the next row/column in order + foreach(const Template & t, dst) { + for (int i=0; i < t.m().cols; i++) + { + output->setRelative(t.m().at(0, i), currentRow, currentCol); + + // row-major input + if (!transposeMode) + currentCol++; + // col-major input + else + currentRow++; + } + // filled in a row, advance to the next, reset column position + if (!transposeMode) { + currentRow++; + currentCol = 0; + } + // filled in a column, advance, reset row + else { + currentCol++; + currentRow = 0; + } + } + + bool blockDone = false; + // In direct mode, we don't buffer rows + if (!transposeMode) + { + currentBlockRow++; + blockDone = true; + } + // in transpose mode, we buffer 100 cols before writing the block + else if (currentCol == bufferedSize) + { + currentBlockCol++; + blockDone = true; + } + else return; + + if (blockDone) + { + // set the next block, only necessary if we haven't buffered the current item + output->setBlock(currentBlockRow, currentBlockCol); + currentRow = 0; + currentCol = 0; + } + } + + void train(const TemplateList& data) + { + (void) data; + } + + void init() + { + if (targetName.isEmpty() || queryName.isEmpty() || outputString.isEmpty()) + return; + + QScopedPointer tGallery(Gallery::make(targetName)); + QScopedPointer qGallery(Gallery::make(queryName)); + + FileList targetFiles = tGallery->files(); + FileList queryFiles = qGallery->files(); + + currentBlockRow = 0; + currentBlockCol = 0; + + currentRow = 0; + currentCol = 0; + + bufferedSize = 100; + + if (transposeMode) + { + // buffer 100 cols at a time + fragmentsPerRow = bufferedSize; + // a single col contains comparisons to all query files + fragmentsPerCol = queryFiles.size(); + } + else + { + // a single row contains comparisons to all target files + fragmentsPerRow = targetFiles.size(); + // we output rows one at a time + fragmentsPerCol = 1; + } + + output = QSharedPointer(Output::make(outputString, targetFiles, queryFiles)); + output->blockRows = fragmentsPerCol; + output->blockCols = fragmentsPerRow; + output->initialize(targetFiles, queryFiles); + + output->setBlock(currentBlockRow, currentBlockCol); + } + + QSharedPointer output; + + int bufferedSize; + + int currentRow; + int currentCol; + + int currentBlockRow; + int currentBlockCol; + + int fragmentsPerRow; + int fragmentsPerCol; + +public: + OutputTransform() : TimeVaryingTransform(false,false) {} +}; + +BR_REGISTER(Transform, OutputTransform) + } #include "misc.moc" diff --git a/openbr/plugins/output.cpp b/openbr/plugins/output.cpp index b9a290a..b58bf49 100644 --- a/openbr/plugins/output.cpp +++ b/openbr/plugins/output.cpp @@ -215,9 +215,11 @@ class mtxOutput : public Output this->rowBlock = rowBlock; this->columnBlock = columnBlock; - blockScores = cv::Mat(std::min(queryFiles.size()-rowBlock*Globals->blockSize, Globals->blockSize), - std::min(targetFiles.size()-columnBlock*Globals->blockSize, Globals->blockSize), - CV_32FC1); + + int matrixRows = std::min(queryFiles.size()-rowBlock*this->blockRows, blockRows); + int matrixCols = std::min(targetFiles.size()-columnBlock*this->blockCols, blockCols); + + blockScores = cv::Mat(matrixRows, matrixCols, CV_32FC1); } void setRelative(float value, int i, int j) @@ -237,7 +239,7 @@ class mtxOutput : public Output if (!f.open(QFile::ReadWrite)) qFatal("Unable to open %s for modifying.", qPrintable(file)); for (int i=0; iblockSize+i)*targetFiles.size()+(columnBlock*Globals->blockSize))); + f.seek(headerSize + sizeof(float)*(quint64(rowBlock*this->blockRows+i)*targetFiles.size()+(columnBlock*this->blockCols))); f.write((const char*)blockScores.row(i).data, sizeof(float)*blockScores.cols); } f.close(); diff --git a/openbr/plugins/pp5.cpp b/openbr/plugins/pp5.cpp index dccf679..0948901 100644 --- a/openbr/plugins/pp5.cpp +++ b/openbr/plugins/pp5.cpp @@ -306,6 +306,22 @@ class PP5CompareDistance : public Distance { Q_OBJECT + struct NativeGallery + { + FileList files; + QList faceIDs; + ppr_gallery_type gallery; + }; + + mutable QMap cache; + mutable QMutex cacheLock; + + ~PP5CompareDistance() + { + foreach (const NativeGallery &gallery, cache.values()) + ppr_free_gallery(gallery.gallery); + } + float compare(const Template &target, const Template &query) const { TemplateList targetList; @@ -315,7 +331,6 @@ class PP5CompareDistance : public Distance MatrixOutput *score = MatrixOutput::make(targetList.files(), queryList.files()); compare(targetList, queryList, score); return score->data.at(0); - } void compare(const TemplateList &target, const TemplateList &query, Output *output) const @@ -326,25 +341,27 @@ class PP5CompareDistance : public Distance QList target_face_ids, query_face_ids; enroll(target, &target_gallery, target_face_ids); enroll(query, &query_gallery, query_face_ids); + compareNative(target_gallery, target_face_ids, query_gallery, query_face_ids, output); + ppr_free_gallery(target_gallery); + ppr_free_gallery(query_gallery); + } - ppr_similarity_matrix_type similarity_matrix; - TRY(ppr_compare_galleries(context, query_gallery, target_gallery, &similarity_matrix)) - - for (int i=0; i &targetIDs, ppr_gallery_type query, const QList &queryIDs, Output *output) const + { + ppr_similarity_matrix_type simmat; + TRY(ppr_compare_galleries(context, query, target, &simmat)) + for (int i=0; i::max(); if ((query_face_id != -1) && (target_face_id != -1)) { - TRY(ppr_get_face_similarity_score(context, similarity_matrix, query_face_id, target_face_id, &score)) + TRY(ppr_get_face_similarity_score(context, simmat, query_face_id, target_face_id, &score)) } output->setRelative(score, i, j); } } - - ppr_free_similarity_matrix(similarity_matrix); - ppr_free_gallery(target_gallery); - ppr_free_gallery(query_gallery); + ppr_free_similarity_matrix(simmat); } void enroll(const TemplateList &templates, ppr_gallery_type *gallery, QList &face_ids) const @@ -363,6 +380,53 @@ class PP5CompareDistance : public Distance } } } + + NativeGallery cacheRetain(const File &gallery) const + { + QMutexLocker locker(&cacheLock); + NativeGallery nativeGallery; + if (cache.contains(gallery.name)) { + nativeGallery = cache[gallery.name]; + } else { + ppr_create_gallery(context, &nativeGallery.gallery); + TemplateList templates = TemplateList::fromGallery(gallery); + enroll(templates, &nativeGallery.gallery, nativeGallery.faceIDs); + nativeGallery.files = templates.files(); + if (gallery.get("retain")) + cache.insert(gallery.name, nativeGallery); + } + return nativeGallery; + } + + void cacheRelease(const File &gallery, const NativeGallery &nativeGallery) const + { + QMutexLocker locker(&cacheLock); + if (cache.contains(gallery.name)) { + if (gallery.get("release")) { + cache.remove(gallery.name); + ppr_free_gallery(nativeGallery.gallery); + } + } else { + ppr_free_gallery(nativeGallery.gallery); + } + } + + bool compare(const File &targetGallery, const File &queryGallery, const File &output) const + { + if (!targetGallery.get("native") || !queryGallery.get("native")) + return false; + + NativeGallery nativeTarget = cacheRetain(targetGallery); + NativeGallery nativeQuery = cacheRetain(queryGallery); + + QScopedPointer o(Output::make(output, nativeTarget.files, nativeQuery.files)); + o->setBlock(0, 0); + compareNative(nativeTarget.gallery, nativeTarget.faceIDs, nativeQuery.gallery, nativeQuery.faceIDs, o.data()); + + cacheRelease(targetGallery, nativeTarget); + cacheRelease(queryGallery, nativeQuery); + return true; + } }; BR_REGISTER(Distance, PP5CompareDistance) diff --git a/openbr/plugins/stream.cpp b/openbr/plugins/stream.cpp index 749c44a..6d106e3 100644 --- a/openbr/plugins/stream.cpp +++ b/openbr/plugins/stream.cpp @@ -25,6 +25,7 @@ class Idiocy : public QObject public: enum StreamModes { StreamVideo, DistributeFrames, + StreamGallery, Auto}; Q_ENUMS(StreamModes) @@ -288,6 +289,75 @@ protected: }; +class StreamGallery : public TemplateProcessor +{ +public: + StreamGallery() + { + + } + + bool open(Template &input) + { + // Create a gallery + gallery = QSharedPointer(Gallery::make(input.file)); + // Failed ot open the gallery? + if (gallery.isNull()) { + qDebug()<<"Failed to create gallery!"; + galleryOk = false; + return false; + } + + // Set up state variables for future reads + galleryOk = true; + gallery->set_readBlockSize(100); + nextIdx = 0; + lastBlock = false; + return galleryOk; + } + + bool isOpen() { return galleryOk; } + + void close() + { + galleryOk = false; + currentData.clear(); + nextIdx = 0; + lastBlock = true; + } + + bool getNextTemplate(Template & output) + { + // If we still have data available, we return one of those + if (nextIdx >= currentData.size()) + { + // Otherwise, read another block + if (!lastBlock) { + currentData = gallery->readBlock(&lastBlock); + nextIdx = 0; + } + else + { + galleryOk = false; + return false; + } + } + // Return the indicated template, and advance the index + output = currentData[nextIdx++]; + return true; + } + +protected: + + QSharedPointer gallery; + bool galleryOk; + bool lastBlock; + + TemplateList currentData; + int nextIdx; + +}; + class DirectReturn : public TemplateProcessor { public: @@ -731,6 +801,11 @@ protected: if (!frameSource) frameSource = new DirectReturn(); } + else if (mode == br::Idiocy::StreamGallery) + { + if (!frameSource) + frameSource = new StreamGallery(); + } else if (mode == br::Idiocy::StreamVideo) { if (!frameSource) { diff --git a/scripts/brpy/__init__.py b/scripts/brpy/__init__.py index 4060fbc..40756a4 100644 --- a/scripts/brpy/__init__.py +++ b/scripts/brpy/__init__.py @@ -21,13 +21,27 @@ def _handle_string_func(func): return call_func def init_brpy(br_loc='/usr/local/lib'): - """Takes the ctypes lib object for br and initializes all function inputs and outputs""" - br_loc += '/libopenbr.%s' - if os.path.exists(br_loc % 'dylib'): - br = cdll.LoadLibrary(br_loc % 'dylib') - elif os.path.exists(br_loc % 'so'): - br = cdll.LoadLibrary(br_loc % 'so') - else: + """Initializes all function inputs and outputs for the br ctypes lib object""" + + lib_path = os.environ.get('LD_LIBRARY_PATH') + paths = [br_loc] + if lib_path: + paths.extend(lib_path.split(':')) + + found = False + for p in paths: + dylib = '%s/%s.%s' % (p, 'libopenbr', 'dylib') + so = '%s/%s.%s' % (p, 'libopenbr', 'so') + if os.path.exists(dylib): + br = cdll.LoadLibrary(dylib) + found = True + break + elif os.path.exists(so): + br = cdll.LoadLibrary(so) + found = True + break + + if not found: raise ValueError('Neither .so nor .dylib libopenbr found in %s' % br_loc) plot_args = _var_string_args(1) + [c_bool] @@ -44,7 +58,7 @@ def init_brpy(br_loc='/usr/local/lib'): br.br_eval.argtypes = _string_args(3) br.br_eval.restype = c_float br.br_eval_classification.argtypes = _string_args(4) - br.br_eval_clustering.argtypes = _string_args(2) + br.br_eval_clustering.argtypes = _string_args(3) br.br_eval_detection.argtypes = _string_args(3) br.br_eval_detection.restype = c_float br.br_eval_landmarking.argtypes = _string_args(3) + [c_int, c_int] @@ -129,7 +143,8 @@ def init_brpy(br_loc='/usr/local/lib'): br.br_make_gallery.restype = c_void_p br.br_load_from_gallery.argtypes = [c_void_p] br.br_load_from_gallery.restype = c_void_p - br.br_add_to_gallery.argtypes = [c_void_p, c_void_p] + br.br_add_template_to_gallery.argtypes = [c_void_p, c_void_p] + br.br_add_template_list_to_gallery.argtypes = [c_void_p, c_void_p] br.br_close_gallery.argtypes = [c_void_p] return br