Commit 1e653ca8191cf843afb85c9367ab079ec0291c57

Authored by Scott Klum
2 parents 8b66426f 99c754ad

Merge branch 'master' of https://github.com/biometrics/openbr

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 &amp;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 &amp;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 &amp;src, TemplateList &amp;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 &amp;src, TemplateList &amp;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 &amp;dst, TemplateList &amp;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 &amp;target, const TemplateList &amp;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&lt;cv::Mat&gt;
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, &copy[j], i, nextTrainableTransform));
113 113 else _projectPartial( &copy[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
... ... @@ -130,7 +130,7 @@ private:
130 130 av[c] = av[c].reshape(1, data.first().m().rows);
131 131 bv[c] = bv[c].reshape(1, data.first().m().rows);
132 132 }
133   - QtUtils::releaseAndWait(futures);
  133 + futures.waitForFinished();
134 134  
135 135 merge(av, a);
136 136 merge(bv, b);
... ...
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], &centers[i]));
417 426 else _train (subdata[i], labels, &subluts[i], &centers[i]);
418 427 }
419   - QtUtils::releaseAndWait(futures);
  428 + futures.waitForFinished();
420 429 }
421 430  
422 431 int getIndex(const Mat &m, const Mat &center) 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
... ...