Commit 1e653ca8191cf843afb85c9367ab079ec0291c57
Merge branch 'master' of https://github.com/biometrics/openbr
Showing
11 changed files
with
221 additions
and
69 deletions
app/br/br.cpp
| ... | ... | @@ -230,10 +230,6 @@ int main(int argc, char *argv[]) |
| 230 | 230 | { |
| 231 | 231 | br_initialize(argc, argv); |
| 232 | 232 | |
| 233 | - // Do argument execution in another thread so this main thread can run an event loop. | |
| 234 | - // When adding fakeMain to the global thread pool we also increment maxThreadCount so | |
| 235 | - // that while this thread waits on parallel work to complete, | |
| 236 | - // the parallel work can make use of all available CPU threads. | |
| 237 | 233 | FakeMain *fakeMain = new FakeMain(argc, argv); |
| 238 | 234 | QThreadPool::globalInstance()->start(fakeMain); |
| 239 | 235 | QCoreApplication::exec(); | ... | ... |
openbr/core/qtutils.h
| ... | ... | @@ -72,13 +72,6 @@ namespace QtUtils |
| 72 | 72 | |
| 73 | 73 | /**** Variant Utilities ****/ |
| 74 | 74 | QString toString(const QVariant &variant); |
| 75 | - | |
| 76 | - inline void releaseAndWait(QFutureSynchronizer<void> & futures) | |
| 77 | - { | |
| 78 | - QThreadPool::globalInstance()->releaseThread(); | |
| 79 | - futures.waitForFinished(); | |
| 80 | - QThreadPool::globalInstance()->reserveThread(); | |
| 81 | - } | |
| 82 | 75 | } |
| 83 | 76 | |
| 84 | 77 | #endif // __QTUTILS_H | ... | ... |
openbr/openbr_plugin.cpp
| ... | ... | @@ -876,6 +876,9 @@ void br::Context::initialize(int &argc, char *argv[], QString sdkPath) |
| 876 | 876 | } |
| 877 | 877 | Globals->sdkPath = sdkPath; |
| 878 | 878 | |
| 879 | + // Empirical evidence suggests an extra thread helps achieve full CPU utilization | |
| 880 | + QThreadPool::globalInstance()->releaseThread(); | |
| 881 | + | |
| 879 | 882 | // Trigger registered initializers |
| 880 | 883 | QList< QSharedPointer<Initializer> > initializers = Factory<Initializer>::makeAll(); |
| 881 | 884 | foreach (const QSharedPointer<Initializer> &initializer, initializers) |
| ... | ... | @@ -884,6 +887,9 @@ void br::Context::initialize(int &argc, char *argv[], QString sdkPath) |
| 884 | 887 | |
| 885 | 888 | void br::Context::finalize() |
| 886 | 889 | { |
| 890 | + // Undo the 'releaseThread()' in 'initialize()' | |
| 891 | + QThreadPool::globalInstance()->reserveThread(); | |
| 892 | + | |
| 887 | 893 | // Trigger registered finalizers |
| 888 | 894 | QList< QSharedPointer<Initializer> > initializers = Factory<Initializer>::makeAll(); |
| 889 | 895 | foreach (const QSharedPointer<Initializer> &initializer, initializers) |
| ... | ... | @@ -1181,7 +1187,7 @@ private: |
| 1181 | 1187 | if (Globals->parallelism) futures.addFuture(QtConcurrent::run(_train, transforms[i], &templatesList[i])); |
| 1182 | 1188 | else _train (transforms[i], &templatesList[i]); |
| 1183 | 1189 | } |
| 1184 | - QtUtils::releaseAndWait(futures); | |
| 1190 | + futures.waitForFinished(); | |
| 1185 | 1191 | } |
| 1186 | 1192 | |
| 1187 | 1193 | void project(const Template &src, Template &dst) const |
| ... | ... | @@ -1295,10 +1301,7 @@ void Transform::project(const TemplateList &src, TemplateList &dst) const |
| 1295 | 1301 | |
| 1296 | 1302 | // There are certain conditions where we should process the templates in serial, |
| 1297 | 1303 | // but generally we'd prefer to process them in parallel. |
| 1298 | - if ((src.size() < 2) || | |
| 1299 | - (QThreadPool::globalInstance()->activeThreadCount() >= QThreadPool::globalInstance()->maxThreadCount()) || | |
| 1300 | - (Globals->parallelism == 0)) { | |
| 1301 | - | |
| 1304 | + if ((src.size() < 2) || (Globals->parallelism == 0)) { | |
| 1302 | 1305 | foreach (const Template &t, src) { |
| 1303 | 1306 | dst.append(Template()); |
| 1304 | 1307 | _project(this, &t, &dst.last()); |
| ... | ... | @@ -1309,7 +1312,7 @@ void Transform::project(const TemplateList &src, TemplateList &dst) const |
| 1309 | 1312 | QFutureSynchronizer<void> futures; |
| 1310 | 1313 | for (int i=0; i<dst.size(); i++) |
| 1311 | 1314 | futures.addFuture(QtConcurrent::run(_project, this, &src[i], &dst[i])); |
| 1312 | - QtUtils::releaseAndWait(futures); | |
| 1315 | + futures.waitForFinished(); | |
| 1313 | 1316 | } |
| 1314 | 1317 | } |
| 1315 | 1318 | |
| ... | ... | @@ -1333,7 +1336,7 @@ void Transform::backProject(const TemplateList &dst, TemplateList &src) const |
| 1333 | 1336 | for (int i=0; i<dst.size(); i++) |
| 1334 | 1337 | if (Globals->parallelism) futures.addFuture(QtConcurrent::run(_backProject, this, &dst[i], &src[i])); |
| 1335 | 1338 | else _backProject (this, &dst[i], &src[i]); |
| 1336 | - QtUtils::releaseAndWait(futures); | |
| 1339 | + futures.waitForFinished(); | |
| 1337 | 1340 | } |
| 1338 | 1341 | |
| 1339 | 1342 | /* Distance - public methods */ |
| ... | ... | @@ -1370,7 +1373,7 @@ void Distance::compare(const TemplateList &target, const TemplateList &query, Ou |
| 1370 | 1373 | if (Globals->parallelism) futures.addFuture(QtConcurrent::run(this, &Distance::compareBlock, targets, queries, output, targetOffset, queryOffset)); |
| 1371 | 1374 | else compareBlock (targets, queries, output, targetOffset, queryOffset); |
| 1372 | 1375 | } |
| 1373 | - QtUtils::releaseAndWait(futures); | |
| 1376 | + futures.waitForFinished(); | |
| 1374 | 1377 | } |
| 1375 | 1378 | |
| 1376 | 1379 | QList<float> Distance::compare(const TemplateList &targets, const Template &query) const | ... | ... |
openbr/openbr_plugin.h
| ... | ... | @@ -304,6 +304,7 @@ struct Template : public QList<cv::Mat> |
| 304 | 304 | Template() {} |
| 305 | 305 | Template(const File &_file) : file(_file) {} /*!< \brief Initialize #file. */ |
| 306 | 306 | Template(const File &_file, const cv::Mat &mat) : file(_file) { append(mat); } /*!< \brief Initialize #file and append a matrix. */ |
| 307 | + Template(const File &_file, const QList<cv::Mat> &mats) : file(_file) { append(mats); } /*!< \brief Initialize #file and append matricies. */ | |
| 307 | 308 | Template(const cv::Mat &mat) { append(mat); } /*!< \brief Append a matrix. */ |
| 308 | 309 | |
| 309 | 310 | inline const cv::Mat &m() const { static const cv::Mat NullMatrix; | ... | ... |
openbr/plugins/distance.cpp
| ... | ... | @@ -160,7 +160,7 @@ class PipeDistance : public Distance |
| 160 | 160 | foreach (br::Distance *distance, distances) |
| 161 | 161 | if (Globals->parallelism) futures.addFuture(QtConcurrent::run(distance, &Distance::train, data)); |
| 162 | 162 | else distance->train(data); |
| 163 | - QtUtils::releaseAndWait(futures); | |
| 163 | + futures.waitForFinished(); | |
| 164 | 164 | } |
| 165 | 165 | |
| 166 | 166 | float compare(const Template &a, const Template &b) const | ... | ... |
openbr/plugins/integral.cpp
| ... | ... | @@ -122,6 +122,147 @@ BR_REGISTER(Transform, IntegralSamplerTransform) |
| 122 | 122 | |
| 123 | 123 | /*! |
| 124 | 124 | * \ingroup transforms |
| 125 | + * \brief Construct template in a recursive decent manner. | |
| 126 | + * \author Josh Klontz \cite jklontz | |
| 127 | + */ | |
| 128 | +class RecursiveIntegralSamplerTransform : public Transform | |
| 129 | +{ | |
| 130 | + Q_OBJECT | |
| 131 | + Q_PROPERTY(int scales READ get_scales WRITE set_scales RESET reset_scales STORED false) | |
| 132 | + Q_PROPERTY(float scaleFactor READ get_scaleFactor WRITE set_scaleFactor RESET reset_scaleFactor STORED false) | |
| 133 | + Q_PROPERTY(int minSize READ get_minSize WRITE set_minSize RESET reset_minSize STORED false) | |
| 134 | + Q_PROPERTY(br::Transform *transform READ get_transform WRITE set_transform RESET reset_transform) | |
| 135 | + BR_PROPERTY(int, scales, 6) | |
| 136 | + BR_PROPERTY(float, scaleFactor, 2) | |
| 137 | + BR_PROPERTY(int, minSize, 8) | |
| 138 | + BR_PROPERTY(br::Transform*, transform, NULL) | |
| 139 | + | |
| 140 | + Transform *subTransform; | |
| 141 | + | |
| 142 | + typedef Eigen::Map< const Eigen::Matrix<qint32,Eigen::Dynamic,1> > InputDescriptor; | |
| 143 | + typedef Eigen::Map< Eigen::Matrix<float,Eigen::Dynamic,1> > OutputDescriptor; | |
| 144 | + typedef Eigen::Map< const Eigen::Matrix<float,Eigen::Dynamic,1> > SecondOrderInputDescriptor; | |
| 145 | + | |
| 146 | + void init() | |
| 147 | + { | |
| 148 | + if (scales >= 2) { | |
| 149 | + File subFile = file; | |
| 150 | + subFile.set("scales", scales-1); | |
| 151 | + subTransform = make(subFile.flat()); | |
| 152 | + } else { | |
| 153 | + subTransform = NULL; | |
| 154 | + } | |
| 155 | + } | |
| 156 | + | |
| 157 | + static void integralHistogram(const Mat &src, const int x, const int y, const int rows, const int columns, Mat &dst, int index) | |
| 158 | + { | |
| 159 | + const int channels = src.channels(); | |
| 160 | + OutputDescriptor(dst.ptr<float>(index), channels, 1) = | |
| 161 | + ( InputDescriptor(src.ptr<qint32>(y+rows, x+columns), channels, 1) | |
| 162 | + - InputDescriptor(src.ptr<qint32>(y, x+columns), channels, 1) | |
| 163 | + - InputDescriptor(src.ptr<qint32>(y+rows, x), channels, 1) | |
| 164 | + + InputDescriptor(src.ptr<qint32>(y, x), channels, 1)).cast<float>()/(rows*columns); | |
| 165 | + } | |
| 166 | + | |
| 167 | + void computeDescriptor(const Mat &src, Mat &dst) const | |
| 168 | + { | |
| 169 | + const int channels = src.channels(); | |
| 170 | + dst = Mat(7, channels, CV_32FC1); | |
| 171 | + const int rows = src.rows-1; // Integral images have an extra row and column | |
| 172 | + const int columns = src.cols-1; | |
| 173 | + integralHistogram(src, 0, 0, rows, columns, dst, 0); | |
| 174 | + | |
| 175 | + Mat tmp(4, channels, CV_32FC1); | |
| 176 | + integralHistogram(src, 0, 0, rows/2, columns/2, tmp, 0); | |
| 177 | + integralHistogram(src, 0, columns/2, rows/2, columns/2, tmp, 1); | |
| 178 | + integralHistogram(src, rows/2, 0, rows/2, columns/2, tmp, 2); | |
| 179 | + integralHistogram(src, rows/2, columns/2, rows/2, columns/2, tmp, 3); | |
| 180 | + OutputDescriptor(dst.ptr<float>(1), channels, 1) = (SecondOrderInputDescriptor(tmp.ptr<float>(0), channels, 1) - SecondOrderInputDescriptor(tmp.ptr<float>(1), channels, 1))/4.f; | |
| 181 | + OutputDescriptor(dst.ptr<float>(2), channels, 1) = (SecondOrderInputDescriptor(tmp.ptr<float>(1), channels, 1) - SecondOrderInputDescriptor(tmp.ptr<float>(3), channels, 1))/4.f; | |
| 182 | + OutputDescriptor(dst.ptr<float>(3), channels, 1) = (SecondOrderInputDescriptor(tmp.ptr<float>(3), channels, 1) - SecondOrderInputDescriptor(tmp.ptr<float>(2), channels, 1))/4.f; | |
| 183 | + OutputDescriptor(dst.ptr<float>(4), channels, 1) = (SecondOrderInputDescriptor(tmp.ptr<float>(2), channels, 1) - SecondOrderInputDescriptor(tmp.ptr<float>(0), channels, 1))/4.f; | |
| 184 | + OutputDescriptor(dst.ptr<float>(5), channels, 1) = (SecondOrderInputDescriptor(tmp.ptr<float>(0), channels, 1) - SecondOrderInputDescriptor(tmp.ptr<float>(3), channels, 1))/4.f; | |
| 185 | + OutputDescriptor(dst.ptr<float>(6), channels, 1) = (SecondOrderInputDescriptor(tmp.ptr<float>(1), channels, 1) - SecondOrderInputDescriptor(tmp.ptr<float>(2), channels, 1))/4.f; | |
| 186 | + } | |
| 187 | + | |
| 188 | + Template subdivide(const Template &src) const | |
| 189 | + { | |
| 190 | + // Integral images have an extra row and column | |
| 191 | + int subWidth = (src.m().cols-1) / scaleFactor + 1; | |
| 192 | + int subHeight = (src.m().rows-1) / scaleFactor + 1; | |
| 193 | + return Template(src.file, QList<Mat>() << Mat(src, Rect(0, 0, subWidth, subHeight)) | |
| 194 | + << Mat(src, Rect(src.m().cols-subWidth, 0, subWidth, subHeight)) | |
| 195 | + << Mat(src, Rect(0, src.m().rows-subHeight, subWidth, subHeight)) | |
| 196 | + << Mat(src, Rect(src.m().cols-subWidth, src.m().rows-subHeight, subWidth, subHeight))); | |
| 197 | + } | |
| 198 | + | |
| 199 | + bool canSubdivide(const Template &t) const | |
| 200 | + { | |
| 201 | + // Integral images have an extra row and column | |
| 202 | + const int subWidth = (t.m().cols-1) / scaleFactor; | |
| 203 | + const int subHeight = (t.m().rows-1) / scaleFactor; | |
| 204 | + return ((subWidth >= minSize) && (subHeight >= minSize)); | |
| 205 | + } | |
| 206 | + | |
| 207 | + void train(const TemplateList &src) | |
| 208 | + { | |
| 209 | + if (subTransform != NULL) { | |
| 210 | + TemplateList subSrc; subSrc.reserve(src.size()); | |
| 211 | + foreach (const Template &t, src) | |
| 212 | + if (canSubdivide(t)) | |
| 213 | + subSrc.append(subdivide(t)); | |
| 214 | + | |
| 215 | + if (subSrc.isEmpty()) { | |
| 216 | + delete subTransform; | |
| 217 | + subTransform = NULL; | |
| 218 | + } else { | |
| 219 | + subTransform->train(subSrc); | |
| 220 | + } | |
| 221 | + } | |
| 222 | + | |
| 223 | + TemplateList dst; dst.reserve(src.size()); | |
| 224 | + foreach (const Template &t, src) { | |
| 225 | + Template u(t.file); | |
| 226 | + computeDescriptor(t, u); | |
| 227 | + dst.append(u); | |
| 228 | + } | |
| 229 | + transform->train(dst); | |
| 230 | + } | |
| 231 | + | |
| 232 | + void project(const Template &src, Template &dst) const | |
| 233 | + { | |
| 234 | + computeDescriptor(src, dst); | |
| 235 | + transform->project(dst, dst); | |
| 236 | + | |
| 237 | + if ((subTransform != NULL) && canSubdivide(src)) { | |
| 238 | + Template subDst; | |
| 239 | + subTransform->project(subdivide(src), subDst); | |
| 240 | + dst.append(subDst); | |
| 241 | + } | |
| 242 | + } | |
| 243 | + | |
| 244 | + void store(QDataStream &stream) const | |
| 245 | + { | |
| 246 | + transform->store(stream); | |
| 247 | + stream << (subTransform == NULL); | |
| 248 | + if (subTransform != NULL) | |
| 249 | + subTransform->store(stream); | |
| 250 | + } | |
| 251 | + | |
| 252 | + void load(QDataStream &stream) | |
| 253 | + { | |
| 254 | + transform->load(stream); | |
| 255 | + bool hasSubTransform; | |
| 256 | + stream >> hasSubTransform; | |
| 257 | + if (hasSubTransform) subTransform->load(stream); | |
| 258 | + else { delete subTransform; subTransform = NULL; } | |
| 259 | + } | |
| 260 | +}; | |
| 261 | + | |
| 262 | +BR_REGISTER(Transform, RecursiveIntegralSamplerTransform) | |
| 263 | + | |
| 264 | +/*! | |
| 265 | + * \ingroup transforms | |
| 125 | 266 | * \brief Computes magnitude and/or angle of image. |
| 126 | 267 | * \author Josh Klontz \cite jklontz |
| 127 | 268 | */ | ... | ... |
openbr/plugins/meta.cpp
| ... | ... | @@ -111,7 +111,7 @@ class PipeTransform : public CompositeTransform |
| 111 | 111 | for (int j=0; j<copy.size(); j++) |
| 112 | 112 | if (Globals->parallelism) futures.addFuture(QtConcurrent::run(this, &PipeTransform::_projectPartial, ©[j], i, nextTrainableTransform)); |
| 113 | 113 | else _projectPartial( ©[j], i, nextTrainableTransform); |
| 114 | - QtUtils::releaseAndWait(futures); | |
| 114 | + futures.waitForFinished(); | |
| 115 | 115 | i = nextTrainableTransform; |
| 116 | 116 | } |
| 117 | 117 | } |
| ... | ... | @@ -293,7 +293,7 @@ class ForkTransform : public CompositeTransform |
| 293 | 293 | if (Globals->parallelism) futures.addFuture(QtConcurrent::run(_train, transforms[i], &data)); |
| 294 | 294 | else _train (transforms[i], &data); |
| 295 | 295 | } |
| 296 | - QtUtils::releaseAndWait(futures); | |
| 296 | + futures.waitForFinished(); | |
| 297 | 297 | } |
| 298 | 298 | |
| 299 | 299 | void backProject(const Template &dst, Template &src) const {Transform::backProject(dst, src);} |
| ... | ... | @@ -648,7 +648,7 @@ public: |
| 648 | 648 | if (Globals->parallelism) futures.addFuture(QtConcurrent::run(_projectList, transform, &input_buffer[i], &output_buffer[i])); |
| 649 | 649 | else _projectList( transform, &input_buffer[i], &output_buffer[i]); |
| 650 | 650 | } |
| 651 | - QtUtils::releaseAndWait(futures); | |
| 651 | + futures.waitForFinished(); | |
| 652 | 652 | |
| 653 | 653 | for (int i=0; i<src.size(); i++) dst.append(output_buffer[i]); |
| 654 | 654 | } | ... | ... |
openbr/plugins/normalize.cpp
openbr/plugins/quantize.cpp
| ... | ... | @@ -158,7 +158,7 @@ class BayesianQuantizationDistance : public Distance |
| 158 | 158 | for (int i=0; i<data.cols; i++) |
| 159 | 159 | if (Globals->parallelism) futures.addFuture(QtConcurrent::run(&BayesianQuantizationDistance::computeLogLikelihood, data.col(i), templateLabels, &loglikelihoods.data()[i*256])); |
| 160 | 160 | else computeLogLikelihood( data.col(i), templateLabels, &loglikelihoods.data()[i*256]); |
| 161 | - QtUtils::releaseAndWait(futures); | |
| 161 | + futures.waitForFinished(); | |
| 162 | 162 | } |
| 163 | 163 | |
| 164 | 164 | float compare(const Template &a, const Template &b) const |
| ... | ... | @@ -259,10 +259,14 @@ class ProductQuantizationDistance : public Distance |
| 259 | 259 | { |
| 260 | 260 | float distance = 0; |
| 261 | 261 | for (int i=0; i<a.size(); i++) { |
| 262 | - const int elements = a[i].total(); | |
| 263 | - const uchar *aData = a[i].data; | |
| 264 | - const uchar *bData = b[i].data; | |
| 265 | - const float *lut = (const float*)ProductQuantizationLUTs[i].data; | |
| 262 | + const int elements = a[i].total()-sizeof(quint16); | |
| 263 | + uchar *aData = a[i].data; | |
| 264 | + uchar *bData = b[i].data; | |
| 265 | + quint16 index = *reinterpret_cast<quint16*>(aData); | |
| 266 | + aData += sizeof(quint16); | |
| 267 | + bData += sizeof(quint16); | |
| 268 | + | |
| 269 | + const float *lut = (const float*)ProductQuantizationLUTs[index].data; | |
| 266 | 270 | for (int j=0; j<elements; j++) |
| 267 | 271 | distance += lut[j*256*256 + aData[j]*256+bData[j]]; |
| 268 | 272 | } |
| ... | ... | @@ -288,12 +292,17 @@ class ProductQuantizationTransform : public Transform |
| 288 | 292 | BR_PROPERTY(br::Distance*, distance, Distance::make("L2", this)) |
| 289 | 293 | BR_PROPERTY(bool, bayesian, false) |
| 290 | 294 | |
| 291 | - int index; | |
| 295 | + quint16 index; | |
| 292 | 296 | QList<Mat> centers; |
| 293 | 297 | |
| 294 | 298 | public: |
| 295 | 299 | ProductQuantizationTransform() |
| 296 | 300 | { |
| 301 | + if (ProductQuantizationLUTs.size() > std::numeric_limits<quint16>::max()) | |
| 302 | + qFatal("Out of LUT space!"); // Unlikely | |
| 303 | + | |
| 304 | + static QMutex mutex; | |
| 305 | + QMutexLocker locker(&mutex); | |
| 297 | 306 | index = ProductQuantizationLUTs.size(); |
| 298 | 307 | ProductQuantizationLUTs.append(Mat()); |
| 299 | 308 | } |
| ... | ... | @@ -416,7 +425,7 @@ private: |
| 416 | 425 | if (Globals->parallelism) futures.addFuture(QtConcurrent::run(this, &ProductQuantizationTransform::_train, subdata[i], labels, &subluts[i], ¢ers[i])); |
| 417 | 426 | else _train (subdata[i], labels, &subluts[i], ¢ers[i]); |
| 418 | 427 | } |
| 419 | - QtUtils::releaseAndWait(futures); | |
| 428 | + futures.waitForFinished(); | |
| 420 | 429 | } |
| 421 | 430 | |
| 422 | 431 | int getIndex(const Mat &m, const Mat ¢er) const |
| ... | ... | @@ -438,19 +447,21 @@ private: |
| 438 | 447 | Mat m = src.m().reshape(1, 1); |
| 439 | 448 | const int step = getStep(m.cols); |
| 440 | 449 | const int offset = getOffset(m.cols); |
| 441 | - dst = Mat(1, getDims(m.cols), CV_8UC1); | |
| 442 | - for (int i=0; i<dst.m().cols; i++) | |
| 443 | - dst.m().at<uchar>(0,i) = getIndex(m.colRange(max(0, i*step-offset), (i+1)*step-offset), centers[i]); | |
| 450 | + const int dims = getDims(m.cols); | |
| 451 | + dst = Mat(1, sizeof(quint16)+dims, CV_8UC1); | |
| 452 | + memcpy(dst.m().data, &index, sizeof(quint16)); | |
| 453 | + for (int i=0; i<dims; i++) | |
| 454 | + dst.m().at<uchar>(0,sizeof(quint16)+i) = getIndex(m.colRange(max(0, i*step-offset), (i+1)*step-offset), centers[i]); | |
| 444 | 455 | } |
| 445 | 456 | |
| 446 | 457 | void store(QDataStream &stream) const |
| 447 | 458 | { |
| 448 | - stream << centers << ProductQuantizationLUTs[index]; | |
| 459 | + stream << index << centers << ProductQuantizationLUTs[index]; | |
| 449 | 460 | } |
| 450 | 461 | |
| 451 | 462 | void load(QDataStream &stream) |
| 452 | 463 | { |
| 453 | - stream >> centers >> ProductQuantizationLUTs[index]; | |
| 464 | + stream >> index >> centers >> ProductQuantizationLUTs[index]; | |
| 454 | 465 | } |
| 455 | 466 | }; |
| 456 | 467 | ... | ... |
openbr/plugins/quantize2.cpp
| ... | ... | @@ -21,47 +21,54 @@ class BayesianQuantizationTransform : public Transform |
| 21 | 21 | Q_OBJECT |
| 22 | 22 | QVector<float> thresholds; |
| 23 | 23 | |
| 24 | + static void computeThresholdsRecursive(const QVector<int> &cumulativeGenuines, const QVector<int> &cumulativeImpostors, | |
| 25 | + float *thresholds, const int thresholdIndex) | |
| 26 | + { | |
| 27 | +// const int totalGenuines = cumulativeGenuines.last()-cumulativeGenuines.first(); | |
| 28 | +// const int totalImpostors = cumulativeImpostors.last()-cumulativeImpostors.first(); | |
| 29 | + | |
| 30 | + int low = 0; | |
| 31 | + int high = cumulativeGenuines.size()-1; | |
| 32 | + int index = cumulativeGenuines.size()/2; | |
| 33 | + | |
| 34 | + while ((index != low) && (index != high)) { | |
| 35 | + index = (high - low)/2; | |
| 36 | +// const float logLikelihoodLow = (float(cumulativeGenuines[index]-cumulativeGenuines.first())/totalGenuines)/ | |
| 37 | +// (float(cumulativeImpostors[index]-cumulativeImpostors.first())/totalImpostors); | |
| 38 | +// const float logLikelihoodHigh = (float(cumulativeGenuines.last()-cumulativeGenuines[index])/totalGenuines)/ | |
| 39 | +// (float(cumulativeImpostors.last()-cumulativeImpostors[index])/totalImpostors); | |
| 40 | + | |
| 41 | + } | |
| 42 | + | |
| 43 | + computeThresholdsRecursive(cumulativeGenuines.mid(0,index), cumulativeImpostors.mid(0,index), thresholds, thresholdIndex); | |
| 44 | + computeThresholdsRecursive(cumulativeGenuines.mid(index), cumulativeImpostors.mid(index), thresholds, thresholdIndex); | |
| 45 | + } | |
| 46 | + | |
| 24 | 47 | static void computeThresholds(const Mat &data, const QList<int> &labels, float *thresholds) |
| 25 | 48 | { |
| 26 | 49 | const QList<float> vals = OpenCVUtils::matrixToVector<float>(data); |
| 27 | 50 | if (vals.size() != labels.size()) |
| 28 | 51 | qFatal("Logic error."); |
| 29 | 52 | |
| 30 | - QList<float> genuineScores; genuineScores.reserve(vals.size()); | |
| 31 | - QList<float> impostorScores; impostorScores.reserve(vals.size()*vals.size()/2); | |
| 53 | + typedef QPair<float,bool> LabeledScore; | |
| 54 | + QList<LabeledScore> labeledScores; labeledScores.reserve(vals.size()); | |
| 32 | 55 | for (int i=0; i<vals.size(); i++) |
| 33 | 56 | for (int j=i+1; j<vals.size(); j++) |
| 34 | - if (labels[i] == labels[j]) genuineScores.append(fabs(vals[i]-vals[j])); | |
| 35 | - else impostorScores.append(fabs(vals[i]-vals[j])); | |
| 36 | - | |
| 37 | - // genuineScores = Common::Downsample(genuineScores, 256); | |
| 38 | - impostorScores = Common::Downsample(impostorScores, genuineScores.size()); | |
| 39 | - double hGenuine = Common::KernelDensityBandwidth(genuineScores); | |
| 40 | - double hImpostor = Common::KernelDensityBandwidth(impostorScores); | |
| 41 | - | |
| 42 | - float genuineMin, genuineMax, impostorMin, impostorMax, min, max; | |
| 43 | - Common::MinMax(genuineScores, &genuineMin, &genuineMax); | |
| 44 | - Common::MinMax(impostorScores, &impostorMin, &impostorMax); | |
| 45 | - min = std::min(genuineMin, impostorMin); | |
| 46 | - max = std::max(genuineMax, impostorMax); | |
| 47 | - qDebug() << genuineMin << genuineMax << impostorMin << impostorMax; | |
| 48 | - | |
| 49 | - QFile g("g.csv"), i("i.csv"), kde("kde.csv"); | |
| 50 | - g.open(QFile::Append); i.open(QFile::Append); kde.open(QFile::Append); | |
| 51 | - | |
| 52 | - QStringList words; | |
| 53 | - const int steps = 256; | |
| 54 | - for (int i=0; i<steps; i++) { | |
| 55 | - const float score = min + i*(max-min)/(steps-1); | |
| 56 | - words.append(QString::number(log(Common::KernelDensityEstimation(genuineScores, score, hGenuine)/ | |
| 57 | - Common::KernelDensityEstimation(impostorScores, score, hImpostor)))); | |
| 57 | + labeledScores.append(LabeledScore(fabs(vals[i]-vals[j]), labels[i] == labels[j])); | |
| 58 | + std::sort(labeledScores.begin(), labeledScores.end()); | |
| 59 | + | |
| 60 | + QVector<int> cumulativeGenuines(labeledScores.size()); | |
| 61 | + QVector<int> cumulativeImpostors(labeledScores.size()); | |
| 62 | + cumulativeGenuines[0] = (labeledScores.first().second ? 1 : 0); | |
| 63 | + cumulativeImpostors[0] = (labeledScores.first().second ? 0 : 1); | |
| 64 | + for (int i=1; i<labeledScores.size(); i++) { | |
| 65 | + cumulativeGenuines[i] = cumulativeGenuines[i-1]; | |
| 66 | + cumulativeImpostors[i] = cumulativeImpostors[i-1]; | |
| 67 | + if (labeledScores.first().second) cumulativeGenuines[i]++; | |
| 68 | + else cumulativeImpostors[i]++; | |
| 58 | 69 | } |
| 59 | 70 | |
| 60 | - g.write(qPrintable(QtUtils::toStringList(genuineScores).join(",")+"\n")); | |
| 61 | - i.write(qPrintable(QtUtils::toStringList(impostorScores).join(",")+"\n")); | |
| 62 | - kde.write(qPrintable(words.join(",")+"\n")); | |
| 63 | - g.close(); i.close(); kde.close(); | |
| 64 | - abort(); | |
| 71 | + computeThresholdsRecursive(cumulativeGenuines, cumulativeImpostors, thresholds, 127); | |
| 65 | 72 | } |
| 66 | 73 | |
| 67 | 74 | void train(const TemplateList &src) |
| ... | ... | @@ -73,7 +80,7 @@ class BayesianQuantizationTransform : public Transform |
| 73 | 80 | |
| 74 | 81 | QFutureSynchronizer<void> futures; |
| 75 | 82 | for (int i=0; i<data.cols; i++) |
| 76 | - if (false) futures.addFuture(QtConcurrent::run(&BayesianQuantizationTransform::computeThresholds, data.col(i), labels, &thresholds.data()[i*256])); | |
| 83 | + if (Globals->parallelism) futures.addFuture(QtConcurrent::run(&BayesianQuantizationTransform::computeThresholds, data.col(i), labels, &thresholds.data()[i*256])); | |
| 77 | 84 | else computeThresholds( data.col(i), labels, &thresholds.data()[i*256]); |
| 78 | 85 | futures.waitForFinished(); |
| 79 | 86 | } | ... | ... |
openbr/plugins/validate.cpp
| ... | ... | @@ -49,7 +49,7 @@ class CrossValidateTransform : public MetaTransform |
| 49 | 49 | if (Globals->parallelism) futures.addFuture(QtConcurrent::run(transforms[i], &Transform::train, partitionedData)); |
| 50 | 50 | else transforms[i]->train(partitionedData); |
| 51 | 51 | } |
| 52 | - QtUtils::releaseAndWait(futures); | |
| 52 | + futures.waitForFinished(); | |
| 53 | 53 | } |
| 54 | 54 | |
| 55 | 55 | void project(const Template &src, Template &dst) const | ... | ... |