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 | + | ... | ... |