Commit 8deca4539b6e05672fd858a3aa5a6a7d5b5c4fb4
Merge pull request #387 from biometrics/RODClustering
Online Rank-Order distance clustering
Showing
1 changed file
with
125 additions
and
0 deletions
openbr/plugins/cluster/onlinerod.cpp
0 → 100644
| 1 | +#include <openbr/plugins/openbr_internal.h> | ||
| 2 | +#include <openbr/core/cluster.h> | ||
| 3 | +#include <openbr/core/opencvutils.h> | ||
| 4 | + | ||
| 5 | +using namespace cv; | ||
| 6 | + | ||
| 7 | +namespace br | ||
| 8 | +{ | ||
| 9 | + | ||
| 10 | +/*! | ||
| 11 | + * \brief Constructors clusters based on the Rank-Order distance in an online, incremental manner | ||
| 12 | + * \author Charles Otto \cite caotto | ||
| 13 | + * \author Jordan Cheney \cite JordanCheney | ||
| 14 | + * \br_property br::Distance* distance Distance to compute the similarity score between templates. Default is L2. | ||
| 15 | + * \br_property int kNN Maximum number of nearest neighbors to keep for each template. Default is 20. | ||
| 16 | + * \br_property float aggression Clustering aggresiveness. A higher value will result in larger clusters. Default is 10. | ||
| 17 | + * \br_property bool incremental If true, compute the clusters as each template is processed otherwise compute the templates at the end. Default is false. | ||
| 18 | + * \br_property QString evalOutput Path to store cluster informtation. Optional. Default is an empty string. | ||
| 19 | + * \br_paper Zhu et al. | ||
| 20 | + * "A Rank-Order Distance based Clustering Algorithm for Face Tagging" | ||
| 21 | + * CVPR 2011 | ||
| 22 | + */ | ||
| 23 | +class OnlineRODTransform : public TimeVaryingTransform | ||
| 24 | +{ | ||
| 25 | + Q_OBJECT | ||
| 26 | + Q_PROPERTY(br::Distance *distance READ get_distance WRITE set_distance RESET reset_distance STORED true) | ||
| 27 | + Q_PROPERTY(int kNN READ get_kNN WRITE set_kNN RESET reset_kNN STORED false) | ||
| 28 | + Q_PROPERTY(float aggression READ get_aggression WRITE set_aggression RESET reset_aggression STORED false) | ||
| 29 | + Q_PROPERTY(bool incremental READ get_incremental WRITE set_incremental RESET reset_incremental STORED false) | ||
| 30 | + Q_PROPERTY(QString evalOutput READ get_evalOutput WRITE set_evalOutput RESET reset_evalOutput STORED false) | ||
| 31 | + | ||
| 32 | + BR_PROPERTY(br::Distance*, distance, Distance::make(".Dist(L2)",this)) | ||
| 33 | + BR_PROPERTY(int, kNN, 20) | ||
| 34 | + BR_PROPERTY(float, aggression, 10) | ||
| 35 | + BR_PROPERTY(bool, incremental, false) | ||
| 36 | + BR_PROPERTY(QString, evalOutput, "") | ||
| 37 | + | ||
| 38 | + TemplateList templates; | ||
| 39 | + Neighborhood neighborhood; | ||
| 40 | + | ||
| 41 | +public: | ||
| 42 | + OnlineRODTransform() : TimeVaryingTransform(false, false) {} | ||
| 43 | + | ||
| 44 | +private: | ||
| 45 | + void projectUpdate(const TemplateList &src, TemplateList &dst) | ||
| 46 | + { | ||
| 47 | + // update current graph | ||
| 48 | + foreach(const Template &t, src) { | ||
| 49 | + QList<float> scores = distance->compare(templates, t); | ||
| 50 | + | ||
| 51 | + // attempt to udpate each existing point's (sorted) k-NN list with these results. | ||
| 52 | + Neighbors currentN; | ||
| 53 | + for (int i=0; i < scores.size(); i++) { | ||
| 54 | + currentN.append(Neighbor(i, scores[i])); | ||
| 55 | + Neighbors target = neighborhood[i]; | ||
| 56 | + | ||
| 57 | + // should we insert the new neighbor into the current target's list? | ||
| 58 | + if (target.size() < kNN || scores[i] > target.last().second) { | ||
| 59 | + // insert into the sorted nearest neighbor list | ||
| 60 | + Neighbor temp(scores.size(), scores[i]); | ||
| 61 | + br::Neighbors::iterator res = qLowerBound(target.begin(), target.end(), temp, compareNeighbors); | ||
| 62 | + target.insert(res, temp); | ||
| 63 | + | ||
| 64 | + if (target.size() > kNN) | ||
| 65 | + target.removeLast(); | ||
| 66 | + | ||
| 67 | + neighborhood[i] = target; | ||
| 68 | + } | ||
| 69 | + } | ||
| 70 | + | ||
| 71 | + // add a new row, consisting of the top neighbors of the newest point | ||
| 72 | + int actuallyKeep = std::min(kNN, currentN.size()); | ||
| 73 | + std::partial_sort(currentN.begin(), currentN.begin()+actuallyKeep, currentN.end(), compareNeighbors); | ||
| 74 | + | ||
| 75 | + Neighbors selected = currentN.mid(0, actuallyKeep); | ||
| 76 | + neighborhood.append(selected); | ||
| 77 | + templates.append(t); | ||
| 78 | + } | ||
| 79 | + | ||
| 80 | + if (incremental) | ||
| 81 | + identifyClusters(dst); | ||
| 82 | + } | ||
| 83 | + | ||
| 84 | + void finalize(TemplateList &output) | ||
| 85 | + { | ||
| 86 | + if (!templates.empty()) { | ||
| 87 | + identifyClusters(output); | ||
| 88 | + templates.clear(); | ||
| 89 | + neighborhood.clear(); | ||
| 90 | + } | ||
| 91 | + } | ||
| 92 | + | ||
| 93 | + void identifyClusters(TemplateList &dst) | ||
| 94 | + { | ||
| 95 | + Clusters clusters = ClusterGraph(neighborhood, aggression, evalOutput); | ||
| 96 | + if (Globals->verbose) | ||
| 97 | + qDebug("Built %d clusters from %d templates", clusters.size(), templates.size()); | ||
| 98 | + | ||
| 99 | + for (int i = 0; i < clusters.size(); i++) { | ||
| 100 | + // Calculate the centroid of each cluster | ||
| 101 | + Mat center = Mat::zeros(templates[0].m().rows, templates[0].m().cols, CV_32F); | ||
| 102 | + foreach (int t, clusters[i]) { | ||
| 103 | + Mat converted; templates[t].m().convertTo(converted, CV_32F); | ||
| 104 | + center += converted; | ||
| 105 | + } | ||
| 106 | + center /= clusters[i].size(); | ||
| 107 | + | ||
| 108 | + // Calculate the Euclidean distance from the center to use as the cluster confidence | ||
| 109 | + foreach (int t, clusters[i]) { | ||
| 110 | + templates[t].file.set("Cluster", i); | ||
| 111 | + Mat c; templates[t].m().convertTo(c, CV_32F); | ||
| 112 | + Mat p; pow(c - center, 2, p); | ||
| 113 | + templates[t].file.set("ClusterConfidence", sqrt(sum(p)[0])); | ||
| 114 | + } | ||
| 115 | + } | ||
| 116 | + dst.append(templates); | ||
| 117 | + } | ||
| 118 | +}; | ||
| 119 | + | ||
| 120 | +BR_REGISTER(Transform, OnlineRODTransform) | ||
| 121 | + | ||
| 122 | +} // namespace br | ||
| 123 | + | ||
| 124 | +#include "onlinerod.moc" | ||
| 125 | + |