Commit a16a89e24d0372bae4757c7f186f7bf5eb87e798
1 parent
b52a58b6
maintainance
Showing
4 changed files
with
75 additions
and
67 deletions
openbr/core/common.h
| @@ -118,22 +118,22 @@ T Max(const QList<T> &vals) | @@ -118,22 +118,22 @@ T Max(const QList<T> &vals) | ||
| 118 | /*! | 118 | /*! |
| 119 | * \brief Returns the mean and standard deviation of a vector of values. | 119 | * \brief Returns the mean and standard deviation of a vector of values. |
| 120 | */ | 120 | */ |
| 121 | -template <typename T> | ||
| 122 | -void Mean(const QList<T> &vals, double *mean) | 121 | +template <template<class> class V, typename T> |
| 122 | +void Mean(const V<T> &vals, double *mean) | ||
| 123 | { | 123 | { |
| 124 | const int size = vals.size(); | 124 | const int size = vals.size(); |
| 125 | 125 | ||
| 126 | // Compute Mean | 126 | // Compute Mean |
| 127 | double sum = 0; | 127 | double sum = 0; |
| 128 | - for (int i=0; i<size; i++) sum += vals[i]; | 128 | + foreach (int val, vals) sum += val; |
| 129 | *mean = (size == 0) ? 0 : sum / size; | 129 | *mean = (size == 0) ? 0 : sum / size; |
| 130 | } | 130 | } |
| 131 | 131 | ||
| 132 | /*! | 132 | /*! |
| 133 | * \brief Returns the mean and standard deviation of a vector of values. | 133 | * \brief Returns the mean and standard deviation of a vector of values. |
| 134 | */ | 134 | */ |
| 135 | -template <typename T> | ||
| 136 | -void MeanStdDev(const QList<T> &vals, double *mean, double *stddev) | 135 | +template <template<class> class V, typename T> |
| 136 | +void MeanStdDev(const V<T> &vals, double *mean, double *stddev) | ||
| 137 | { | 137 | { |
| 138 | const int size = vals.size(); | 138 | const int size = vals.size(); |
| 139 | 139 | ||
| @@ -141,8 +141,8 @@ void MeanStdDev(const QList<T> &vals, double *mean, double *stddev) | @@ -141,8 +141,8 @@ void MeanStdDev(const QList<T> &vals, double *mean, double *stddev) | ||
| 141 | 141 | ||
| 142 | // Compute Standard Deviation | 142 | // Compute Standard Deviation |
| 143 | double variance = 0; | 143 | double variance = 0; |
| 144 | - for (int i=0; i<size; i++) { | ||
| 145 | - double delta = vals[i] - *mean; | 144 | + foreach (T val, vals) { |
| 145 | + const double delta = val - *mean; | ||
| 146 | variance += delta * delta; | 146 | variance += delta * delta; |
| 147 | } | 147 | } |
| 148 | *stddev = (size == 0) ? 0 : sqrt(variance/size); | 148 | *stddev = (size == 0) ? 0 : sqrt(variance/size); |
| @@ -193,8 +193,8 @@ QList<T> CumSum(const QList<T> &vals) | @@ -193,8 +193,8 @@ QList<T> CumSum(const QList<T> &vals) | ||
| 193 | /*! | 193 | /*! |
| 194 | * \brief Calculate DKE bandwidth parameter 'h' | 194 | * \brief Calculate DKE bandwidth parameter 'h' |
| 195 | */ | 195 | */ |
| 196 | -template <typename T> | ||
| 197 | -double KernelDensityBandwidth(const QList<T> &vals) | 196 | +template <template<class> class V, typename T> |
| 197 | +double KernelDensityBandwidth(const V<T> &vals) | ||
| 198 | { | 198 | { |
| 199 | double mean, stddev; | 199 | double mean, stddev; |
| 200 | MeanStdDev(vals, &mean, &stddev); | 200 | MeanStdDev(vals, &mean, &stddev); |
| @@ -204,8 +204,8 @@ double KernelDensityBandwidth(const QList<T> &vals) | @@ -204,8 +204,8 @@ double KernelDensityBandwidth(const QList<T> &vals) | ||
| 204 | /*! | 204 | /*! |
| 205 | * \brief Compute kernel density at value x with bandwidth h. | 205 | * \brief Compute kernel density at value x with bandwidth h. |
| 206 | */ | 206 | */ |
| 207 | -template <typename T> | ||
| 208 | -double KernelDensityEstimation(const QList<T> &vals, double x, double h) | 207 | +template <template<class> class V, typename T> |
| 208 | +double KernelDensityEstimation(const V<T> &vals, double x, double h) | ||
| 209 | { | 209 | { |
| 210 | double y = 0; | 210 | double y = 0; |
| 211 | foreach (T val, vals) | 211 | foreach (T val, vals) |
| @@ -320,16 +320,15 @@ QList<T> RemoveOutliers(QList<T> vals) | @@ -320,16 +320,15 @@ QList<T> RemoveOutliers(QList<T> vals) | ||
| 320 | /*! | 320 | /*! |
| 321 | * \brief Sorts and evenly downsamples a vector to size k. | 321 | * \brief Sorts and evenly downsamples a vector to size k. |
| 322 | */ | 322 | */ |
| 323 | -template <typename T> | ||
| 324 | -QList<T> Downsample(QList<T> vals, long k) | 323 | +template <template<class> class V, typename T> |
| 324 | +V<T> Downsample(V<T> vals, int k) | ||
| 325 | { | 325 | { |
| 326 | - // Use 'long' instead of 'int' so multiplication doesn't overflow | ||
| 327 | - qSort(vals); | ||
| 328 | - long size = (long)vals.size(); | 326 | + std::sort(vals.begin(), vals.end()); |
| 327 | + int size = vals.size(); | ||
| 329 | if (size <= k) return vals; | 328 | if (size <= k) return vals; |
| 330 | 329 | ||
| 331 | - QList<T> newVals; newVals.reserve(k); | ||
| 332 | - for (long i=0; i<k; i++) newVals.push_back(vals[i * (size-1) / (k-1)]); | 330 | + V<T> newVals; newVals.reserve(k); |
| 331 | + for (int i=0; i<k; i++) newVals.push_back(vals[long(i) * long(size-1) / long(k-1)]); | ||
| 333 | return newVals; | 332 | return newVals; |
| 334 | } | 333 | } |
| 335 | 334 |
openbr/plugins/algorithms.cpp
| @@ -42,7 +42,7 @@ class AlgorithmsInitializer : public Initializer | @@ -42,7 +42,7 @@ class AlgorithmsInitializer : public Initializer | ||
| 42 | Globals->abbreviations.insert("OpenBR", "FaceRecognition"); | 42 | Globals->abbreviations.insert("OpenBR", "FaceRecognition"); |
| 43 | Globals->abbreviations.insert("GenderEstimation", "GenderClassification"); | 43 | Globals->abbreviations.insert("GenderEstimation", "GenderClassification"); |
| 44 | Globals->abbreviations.insert("AgeEstimation", "AgeRegression"); | 44 | Globals->abbreviations.insert("AgeEstimation", "AgeRegression"); |
| 45 | - Globals->abbreviations.insert("FaceRecognitionHoG", "Open+Cvt(Gray)+Cascade(FrontalFace)+ASEFEyes+Affine(64,64,0.25,0.35)+Gradient+Bin(0,360,8,true)+Merge+Integral+IntegralSampler+RootNorm+ProductQuantization(2,true):ProductQuantization(true)"); | 45 | + Globals->abbreviations.insert("FaceRecognitionHoG", "Open+Cvt(Gray)+Cascade(FrontalFace)+ASEFEyes+Affine(64,64,0.25,0.35)+Gradient+Bin(0,360,8,true)+Merge+Integral+IntegralSampler+ProductQuantization(2,L1,true):ProductQuantization(true)"); |
| 46 | 46 | ||
| 47 | // Generic Image Processing | 47 | // Generic Image Processing |
| 48 | Globals->abbreviations.insert("SIFT", "Open+KeyPointDetector(SIFT)+KeyPointDescriptor(SIFT):KeyPointMatcher(BruteForce)"); | 48 | Globals->abbreviations.insert("SIFT", "Open+KeyPointDetector(SIFT)+KeyPointDescriptor(SIFT):KeyPointMatcher(BruteForce)"); |
openbr/plugins/distance.cpp
| @@ -37,6 +37,7 @@ class DistDistance : public Distance | @@ -37,6 +37,7 @@ class DistDistance : public Distance | ||
| 37 | Q_OBJECT | 37 | Q_OBJECT |
| 38 | Q_ENUMS(Metric) | 38 | Q_ENUMS(Metric) |
| 39 | Q_PROPERTY(Metric metric READ get_metric WRITE set_metric RESET reset_metric STORED false) | 39 | Q_PROPERTY(Metric metric READ get_metric WRITE set_metric RESET reset_metric STORED false) |
| 40 | + Q_PROPERTY(bool negLogPlusOne READ get_negLogPlusOne WRITE set_negLogPlusOne RESET reset_negLogPlusOne STORED false) | ||
| 40 | 41 | ||
| 41 | public: | 42 | public: |
| 42 | /*!< */ | 43 | /*!< */ |
| @@ -51,6 +52,7 @@ public: | @@ -51,6 +52,7 @@ public: | ||
| 51 | 52 | ||
| 52 | private: | 53 | private: |
| 53 | BR_PROPERTY(Metric, metric, L2) | 54 | BR_PROPERTY(Metric, metric, L2) |
| 55 | + BR_PROPERTY(bool, negLogPlusOne, true) | ||
| 54 | 56 | ||
| 55 | float compare(const Template &a, const Template &b) const | 57 | float compare(const Template &a, const Template &b) const |
| 56 | { | 58 | { |
| @@ -61,8 +63,7 @@ private: | @@ -61,8 +63,7 @@ private: | ||
| 61 | float result = std::numeric_limits<float>::max(); | 63 | float result = std::numeric_limits<float>::max(); |
| 62 | switch (metric) { | 64 | switch (metric) { |
| 63 | case Correlation: | 65 | case Correlation: |
| 64 | - result = -compareHist(a, b, CV_COMP_CORREL); | ||
| 65 | - break; | 66 | + return compareHist(a, b, CV_COMP_CORREL); |
| 66 | case ChiSquared: | 67 | case ChiSquared: |
| 67 | result = compareHist(a, b, CV_COMP_CHISQR); | 68 | result = compareHist(a, b, CV_COMP_CHISQR); |
| 68 | break; | 69 | break; |
| @@ -82,8 +83,7 @@ private: | @@ -82,8 +83,7 @@ private: | ||
| 82 | result = norm(a, b, NORM_L2); | 83 | result = norm(a, b, NORM_L2); |
| 83 | break; | 84 | break; |
| 84 | case Cosine: | 85 | case Cosine: |
| 85 | - result = cosine(a, b); | ||
| 86 | - break; | 86 | + return cosine(a, b); |
| 87 | default: | 87 | default: |
| 88 | qFatal("Invalid metric"); | 88 | qFatal("Invalid metric"); |
| 89 | } | 89 | } |
| @@ -91,7 +91,7 @@ private: | @@ -91,7 +91,7 @@ private: | ||
| 91 | if (result != result) | 91 | if (result != result) |
| 92 | qFatal("NaN result."); | 92 | qFatal("NaN result."); |
| 93 | 93 | ||
| 94 | - return -log(result+1); | 94 | + return negLogPlusOne ? -log(result+1) : result; |
| 95 | } | 95 | } |
| 96 | 96 | ||
| 97 | static float cosine(const Mat &a, const Mat &b) | 97 | static float cosine(const Mat &a, const Mat &b) |
openbr/plugins/quantize.cpp
| @@ -18,6 +18,7 @@ | @@ -18,6 +18,7 @@ | ||
| 18 | #include <QtConcurrentRun> | 18 | #include <QtConcurrentRun> |
| 19 | #include <openbr/openbr_plugin.h> | 19 | #include <openbr/openbr_plugin.h> |
| 20 | 20 | ||
| 21 | +#include "openbr/core/common.h" | ||
| 21 | #include "openbr/core/opencvutils.h" | 22 | #include "openbr/core/opencvutils.h" |
| 22 | 23 | ||
| 23 | using namespace cv; | 24 | using namespace cv; |
| @@ -133,7 +134,7 @@ class ProductQuantizationDistance : public Distance | @@ -133,7 +134,7 @@ class ProductQuantizationDistance : public Distance | ||
| 133 | const uchar *bData = b[i].data; | 134 | const uchar *bData = b[i].data; |
| 134 | const float *lut = (const float*)ProductQuantizationLUTs[i].data; | 135 | const float *lut = (const float*)ProductQuantizationLUTs[i].data; |
| 135 | for (int j=0; j<elements; j++) | 136 | for (int j=0; j<elements; j++) |
| 136 | - distance += lut[i*256*256 + aData[j]*256+bData[j]]; | 137 | + distance += lut[j*256*256 + aData[j]*256+bData[j]]; |
| 137 | } | 138 | } |
| 138 | if (!bayesian) distance = -log(distance+1); | 139 | if (!bayesian) distance = -log(distance+1); |
| 139 | return distance; | 140 | return distance; |
| @@ -151,8 +152,10 @@ class ProductQuantizationTransform : public Transform | @@ -151,8 +152,10 @@ class ProductQuantizationTransform : public Transform | ||
| 151 | { | 152 | { |
| 152 | Q_OBJECT | 153 | Q_OBJECT |
| 153 | Q_PROPERTY(int n READ get_n WRITE set_n RESET reset_n STORED false) | 154 | Q_PROPERTY(int n READ get_n WRITE set_n RESET reset_n STORED false) |
| 155 | + Q_PROPERTY(br::Distance *distance READ get_distance WRITE set_distance RESET reset_distance STORED false) | ||
| 154 | Q_PROPERTY(bool bayesian READ get_bayesian WRITE set_bayesian RESET reset_bayesian STORED false) | 156 | Q_PROPERTY(bool bayesian READ get_bayesian WRITE set_bayesian RESET reset_bayesian STORED false) |
| 155 | BR_PROPERTY(int, n, 2) | 157 | BR_PROPERTY(int, n, 2) |
| 158 | + BR_PROPERTY(br::Distance*, distance, Distance::make("L2", this)) | ||
| 156 | BR_PROPERTY(bool, bayesian, false) | 159 | BR_PROPERTY(bool, bayesian, false) |
| 157 | 160 | ||
| 158 | int index; | 161 | int index; |
| @@ -166,76 +169,82 @@ public: | @@ -166,76 +169,82 @@ public: | ||
| 166 | } | 169 | } |
| 167 | 170 | ||
| 168 | private: | 171 | private: |
| 169 | - static double likelihoodRatio(const QPair<int,int> &totals, const QList<int> &targets, const QList<int> &queries) | 172 | + void _train(const Mat &data, const QList<int> &labels, Mat *lut, Mat *center) |
| 170 | { | 173 | { |
| 171 | - int positives = 1, negatives = 1; // Equal priors | ||
| 172 | - foreach (int t, targets) | ||
| 173 | - foreach (int q, queries) | ||
| 174 | - if (t == q) positives++; | ||
| 175 | - else negatives++; | ||
| 176 | - return log((float(positives)/float(totals.first)) / (float(negatives)/float(totals.second))); | ||
| 177 | - } | 174 | + Mat clusterLabels; |
| 175 | + kmeans(data, 256, clusterLabels, TermCriteria(TermCriteria::MAX_ITER, 10, 0), 3, KMEANS_PP_CENTERS, *center); | ||
| 178 | 176 | ||
| 179 | - void _train(const Mat &data, const QPair<int,int> &totals, Mat &lut, int i, const QList<int> &templateLabels) | ||
| 180 | - { | ||
| 181 | - Mat labels, center; | ||
| 182 | - kmeans(data.colRange(i*n,(i+1)*n), 256, labels, TermCriteria(TermCriteria::MAX_ITER, 10, 0), 3, KMEANS_PP_CENTERS, center); | ||
| 183 | - QList<int> clusterLabels = OpenCVUtils::matrixToVector<int>(labels); | 177 | + for (int j=0; j<256; j++) |
| 178 | + for (int k=0; k<256; k++) | ||
| 179 | + lut->at<float>(0,j*256+k) = distance->compare(center->row(j), center->row(k)); | ||
| 180 | + | ||
| 181 | + if (!bayesian) return; | ||
| 182 | + | ||
| 183 | + QList<int> indicies = OpenCVUtils::matrixToVector<int>(clusterLabels); | ||
| 184 | + QVector<float> genuineScores; genuineScores.reserve(data.rows); | ||
| 185 | + QVector<float> impostorScores; impostorScores.reserve(data.rows*data.rows/2); | ||
| 186 | + for (int i=0; i<indicies.size(); i++) | ||
| 187 | + for (int j=i+1; j<indicies.size(); j++) { | ||
| 188 | + const float score = lut->at<float>(0, indicies[i]*256+indicies[j]); | ||
| 189 | + if (labels[i] == labels[j]) genuineScores.append(score); | ||
| 190 | + else impostorScores.append(score); | ||
| 191 | + } | ||
| 192 | + genuineScores = Common::Downsample(genuineScores, 256); | ||
| 193 | + impostorScores = Common::Downsample(impostorScores, 256); | ||
| 184 | 194 | ||
| 185 | - QHash< int, QList<int> > clusters; // QHash<clusterLabel, QList<templateLabel>> | ||
| 186 | - for (int j=0; j<clusterLabels.size(); j++) | ||
| 187 | - clusters[clusterLabels[j]].append(templateLabels[j]); | 195 | + double hGenuine = Common::KernelDensityBandwidth(genuineScores); |
| 196 | + double hImpostor = Common::KernelDensityBandwidth(impostorScores); | ||
| 188 | 197 | ||
| 189 | for (int j=0; j<256; j++) | 198 | for (int j=0; j<256; j++) |
| 190 | for (int k=0; k<256; k++) | 199 | for (int k=0; k<256; k++) |
| 191 | - lut.at<float>(i,j*256+k) = bayesian ? likelihoodRatio(totals, clusters[j], clusters[k]) : | ||
| 192 | - norm(center.row(j), center.row(k), NORM_L2); | ||
| 193 | - centers[i] = center; | 200 | + lut->at<float>(0,j*256+k) = log(Common::KernelDensityEstimation(genuineScores, lut->at<float>(0,j*256+k), hGenuine) / |
| 201 | + Common::KernelDensityEstimation(impostorScores, lut->at<float>(0,j*256+k), hImpostor)); | ||
| 194 | } | 202 | } |
| 195 | 203 | ||
| 196 | void train(const TemplateList &src) | 204 | void train(const TemplateList &src) |
| 197 | { | 205 | { |
| 198 | Mat data = OpenCVUtils::toMat(src.data()); | 206 | Mat data = OpenCVUtils::toMat(src.data()); |
| 199 | if (data.cols % n != 0) qFatal("Expected dimensionality to be divisible by n."); | 207 | if (data.cols % n != 0) qFatal("Expected dimensionality to be divisible by n."); |
| 200 | - const QList<int> templateLabels = src.labels<int>(); | ||
| 201 | - int totalPositives = 0, totalNegatives = 0; | ||
| 202 | - for (int i=0; i<templateLabels.size(); i++) | ||
| 203 | - for (int j=0; j<templateLabels.size(); j++) | ||
| 204 | - if (templateLabels[i] == templateLabels[j]) totalPositives++; | ||
| 205 | - else totalNegatives++; | ||
| 206 | - QPair<int,int> totals(totalPositives, totalNegatives); | 208 | + const QList<int> labels = src.labels<int>(); |
| 207 | 209 | ||
| 208 | Mat &lut = ProductQuantizationLUTs[index]; | 210 | Mat &lut = ProductQuantizationLUTs[index]; |
| 209 | lut = Mat(data.cols/n, 256*256, CV_32FC1); | 211 | lut = Mat(data.cols/n, 256*256, CV_32FC1); |
| 210 | 212 | ||
| 211 | - for (int i=0; i<lut.rows; i++) | 213 | + QList<Mat> subdata, subluts; |
| 214 | + for (int i=0; i<lut.rows; i++) { | ||
| 212 | centers.append(Mat()); | 215 | centers.append(Mat()); |
| 216 | + subdata.append(data.colRange(i*n,(i+1)*n)); | ||
| 217 | + subluts.append(lut.row(i)); | ||
| 218 | + } | ||
| 213 | 219 | ||
| 214 | QFutureSynchronizer<void> futures; | 220 | QFutureSynchronizer<void> futures; |
| 215 | for (int i=0; i<lut.rows; i++) { | 221 | for (int i=0; i<lut.rows; i++) { |
| 216 | - if (Globals->parallelism) futures.addFuture(QtConcurrent::run(this, &ProductQuantizationTransform::_train, data, totals, lut, i, templateLabels)); | ||
| 217 | - else _train (data, totals, lut, i, templateLabels); | 222 | + if (Globals->parallelism) futures.addFuture(QtConcurrent::run(this, &ProductQuantizationTransform::_train, subdata[i], labels, &subluts[i], ¢ers[i])); |
| 223 | + else _train (subdata[i], labels, &subluts[i], ¢ers[i]); | ||
| 218 | } | 224 | } |
| 219 | futures.waitForFinished(); | 225 | futures.waitForFinished(); |
| 220 | } | 226 | } |
| 221 | 227 | ||
| 228 | + int getIndex(const Mat &m, const Mat ¢er) const | ||
| 229 | + { | ||
| 230 | + int bestIndex = 0; | ||
| 231 | + double bestDistance = std::numeric_limits<double>::max(); | ||
| 232 | + for (int j=0; j<256; j++) { | ||
| 233 | + double distance = norm(m, center.row(j), NORM_L2); | ||
| 234 | + if (distance < bestDistance) { | ||
| 235 | + bestDistance = distance; | ||
| 236 | + bestIndex = j; | ||
| 237 | + } | ||
| 238 | + } | ||
| 239 | + return bestIndex; | ||
| 240 | + } | ||
| 241 | + | ||
| 222 | void project(const Template &src, Template &dst) const | 242 | void project(const Template &src, Template &dst) const |
| 223 | { | 243 | { |
| 224 | Mat m = src.m().reshape(1, 1); | 244 | Mat m = src.m().reshape(1, 1); |
| 225 | dst = Mat(1, m.cols/n, CV_8UC1); | 245 | dst = Mat(1, m.cols/n, CV_8UC1); |
| 226 | - for (int i=0; i<dst.m().cols; i++) { | ||
| 227 | - int bestIndex = 0; | ||
| 228 | - double bestDistance = std::numeric_limits<double>::max(); | ||
| 229 | - Mat m_i = m.colRange(i*n, (i+1)*n); | ||
| 230 | - for (int j=0; j<256; j++) { | ||
| 231 | - double distance = norm(m_i, centers[index].row(j), NORM_L2); | ||
| 232 | - if (distance < bestDistance) { | ||
| 233 | - bestDistance = distance; | ||
| 234 | - bestIndex = j; | ||
| 235 | - } | ||
| 236 | - } | ||
| 237 | - dst.m().at<uchar>(0,i) = bestIndex; | ||
| 238 | - } | 246 | + for (int i=0; i<dst.m().cols; i++) |
| 247 | + dst.m().at<uchar>(0,i) = getIndex(m.colRange(i*n, (i+1)*n), centers[i]); | ||
| 239 | } | 248 | } |
| 240 | 249 | ||
| 241 | void store(QDataStream &stream) const | 250 | void store(QDataStream &stream) const |