Commit df64be5e2716b969ffe441fa79825e625c80b7d9

Authored by Scott Klum
1 parent 4691ca21

Revert "Revert "Merge pull request #320 from biometrics/block_compression""

This reverts commit decda7bc5277e839d1bcf3e78754f2400878c969.
openbr/core/common.cpp
@@ -16,21 +16,32 @@ @@ -16,21 +16,32 @@
16 16
17 #include "common.h" 17 #include "common.h"
18 #include <QMutex> 18 #include <QMutex>
  19 +#include <RandomLib/Random.hpp>
19 20
20 using namespace std; 21 using namespace std;
21 22
  23 +static RandomLib::Random g_rand;
  24 +static QMutex rngLock;
  25 +
22 /**** GLOBAL ****/ 26 /**** GLOBAL ****/
23 void Common::seedRNG() { 27 void Common::seedRNG() {
24 - static QMutex seedControl;  
25 - QMutexLocker lock(&seedControl); 28 + QMutexLocker lock(&rngLock);
26 29
27 static bool seeded = false; 30 static bool seeded = false;
28 if (!seeded) { 31 if (!seeded) {
29 srand(0); // We seed with 0 instead of time(NULL) to have reproducible randomness 32 srand(0); // We seed with 0 instead of time(NULL) to have reproducible randomness
30 seeded = true; 33 seeded = true;
  34 + g_rand.Reseed(0);
31 } 35 }
32 } 36 }
33 37
  38 +double Common::randN()
  39 +{
  40 + QMutexLocker lock(&rngLock);
  41 +
  42 + return g_rand.FloatN();
  43 +}
  44 +
34 QList<int> Common::RandSample(int n, int max, int min, bool unique) 45 QList<int> Common::RandSample(int n, int max, int min, bool unique)
35 { 46 {
36 QList<int> samples; samples.reserve(n); 47 QList<int> samples; samples.reserve(n);
openbr/core/common.h
@@ -220,6 +220,9 @@ double KernelDensityEstimation(const V&lt;T&gt; &amp;vals, double x, double h) @@ -220,6 +220,9 @@ double KernelDensityEstimation(const V&lt;T&gt; &amp;vals, double x, double h)
220 return y / (vals.size() * h); 220 return y / (vals.size() * h);
221 } 221 }
222 222
  223 +// Return a random number, uniformly distributed over 0,1
  224 +double randN();
  225 +
223 /*! 226 /*!
224 * \brief Returns a vector of n integers sampled in the range <min, max]. 227 * \brief Returns a vector of n integers sampled in the range <min, max].
225 * 228 *
@@ -236,19 +239,14 @@ QList&lt;int&gt; RandSample(int n, const QSet&lt;int&gt; &amp;values, bool unique = false); @@ -236,19 +239,14 @@ QList&lt;int&gt; RandSample(int n, const QSet&lt;int&gt; &amp;values, bool unique = false);
236 template <typename T> 239 template <typename T>
237 QList<int> RandSample(int n, const QList<T> &weights, bool unique = false) 240 QList<int> RandSample(int n, const QList<T> &weights, bool unique = false)
238 { 241 {
239 - static bool seeded = false;  
240 - if (!seeded) {  
241 - srand(time(NULL));  
242 - seeded = true;  
243 - }  
244 -  
245 QList<T> cdf = CumSum(weights); 242 QList<T> cdf = CumSum(weights);
246 for (int i=0; i<cdf.size(); i++) // Normalize cdf 243 for (int i=0; i<cdf.size(); i++) // Normalize cdf
247 cdf[i] = cdf[i] / cdf.last(); 244 cdf[i] = cdf[i] / cdf.last();
248 245
249 QList<int> samples; samples.reserve(n); 246 QList<int> samples; samples.reserve(n);
250 while (samples.size() < n) { 247 while (samples.size() < n) {
251 - T r = (T)rand() / (T)RAND_MAX; 248 + T r = randN();
  249 +
252 for (int j=0; j<weights.size(); j++) { 250 for (int j=0; j<weights.size(); j++) {
253 if ((r >= cdf[j]) && (r <= cdf[j+1])) { 251 if ((r >= cdf[j]) && (r <= cdf[j+1])) {
254 if (!unique || !samples.contains(j)) 252 if (!unique || !samples.contains(j))
openbr/core/core.cpp
@@ -110,9 +110,11 @@ struct AlgorithmCore @@ -110,9 +110,11 @@ struct AlgorithmCore
110 110
111 void store(const QString &model) const 111 void store(const QString &model) const
112 { 112 {
113 - // Create stream  
114 - QByteArray data;  
115 - QDataStream out(&data, QFile::WriteOnly); 113 + QtUtils::BlockCompression compressedWrite;
  114 + QFile outFile(model);
  115 + compressedWrite.setBasis(&outFile);
  116 + QDataStream out(&compressedWrite);
  117 + compressedWrite.open(QFile::WriteOnly);
116 118
117 // Serialize algorithm to stream 119 // Serialize algorithm to stream
118 transform->serialize(out); 120 transform->serialize(out);
@@ -131,18 +133,16 @@ struct AlgorithmCore @@ -131,18 +133,16 @@ struct AlgorithmCore
131 if (mode == TransformCompare) 133 if (mode == TransformCompare)
132 comparison->serialize(out); 134 comparison->serialize(out);
133 135
134 - // Compress and save to file  
135 - QtUtils::writeFile(model, data, -1); 136 + compressedWrite.close();
136 } 137 }
137 138
138 void load(const QString &model) 139 void load(const QString &model)
139 { 140 {
140 - // Load from file and decompress  
141 - QByteArray data;  
142 - QtUtils::readFile(model, data, true);  
143 -  
144 - // Create stream  
145 - QDataStream in(&data, QFile::ReadOnly); 141 + QtUtils::BlockCompression compressedRead;
  142 + QFile inFile(model);
  143 + compressedRead.setBasis(&inFile);
  144 + QDataStream in(&compressedRead);
  145 + compressedRead.open(QFile::ReadOnly);
146 146
147 // Load algorithm 147 // Load algorithm
148 transform = QSharedPointer<Transform>(Transform::deserialize(in)); 148 transform = QSharedPointer<Transform>(Transform::deserialize(in));
openbr/core/qtutils.cpp
@@ -500,6 +500,131 @@ QString getAbsolutePath(const QString &amp;filename) @@ -500,6 +500,131 @@ QString getAbsolutePath(const QString &amp;filename)
500 return QFileInfo(filename).absoluteFilePath(); 500 return QFileInfo(filename).absoluteFilePath();
501 } 501 }
502 502
  503 +BlockCompression::BlockCompression(QIODevice *_basis)
  504 +{
  505 + blockSize = 100000000;
  506 + setBasis(_basis);
  507 +}
  508 +
  509 +BlockCompression::BlockCompression() { blockSize = 100000000; };
  510 +
  511 +
  512 +bool BlockCompression::open(QIODevice::OpenMode mode)
  513 +{
  514 + this->setOpenMode(mode);
  515 + bool res = basis->open(mode);
  516 +
  517 + if (!res)
  518 + return false;
  519 +
  520 + blockReader.setDevice(basis);
  521 + blockWriter.setDevice(basis);
  522 +
  523 + if (mode & QIODevice::WriteOnly) {
  524 + precompressedBlockWriter = new QBuffer;
  525 + precompressedBlockWriter->open(QIODevice::ReadWrite);
  526 + }
  527 + else if (mode & QIODevice::ReadOnly) {
  528 + QByteArray compressedBlock;
  529 + blockReader >> compressedBlock;
  530 +
  531 + decompressedBlock = qUncompress(compressedBlock);
  532 + decompressedBlockReader.setBuffer(&decompressedBlock);
  533 + decompressedBlockReader.open(QIODevice::ReadOnly);
  534 + }
  535 +
  536 + return true;
  537 +}
  538 +
  539 +void BlockCompression::close()
  540 +{
  541 + // flush output buffer
  542 + if ((openMode() & QIODevice::WriteOnly) && precompressedBlockWriter) {
  543 + QByteArray compressedBlock = qCompress(precompressedBlockWriter->buffer(), -1);
  544 + blockWriter << compressedBlock;
  545 + }
  546 + basis->close();
  547 +}
  548 +
  549 +void BlockCompression::setBasis(QIODevice *_basis)
  550 +{
  551 + basis = _basis;
  552 + blockReader.setDevice(basis);
  553 + blockWriter.setDevice(basis);
  554 +}
  555 +
  556 +// read from current decompressed block, if out of space, read and decompress another
  557 +// block from basis
  558 +qint64 BlockCompression::readData(char *data, qint64 remaining)
  559 +{
  560 + qint64 read = 0;
  561 + while (remaining > 0) {
  562 + qint64 single_read = decompressedBlockReader.read(data, remaining);
  563 + if (single_read == -1)
  564 + qFatal("miss read");
  565 +
  566 + remaining -= single_read;
  567 + read += single_read;
  568 + data += single_read;
  569 +
  570 + // need a new block
  571 + if (remaining > 0) {
  572 + QByteArray compressedBlock;
  573 + blockReader >> compressedBlock;
  574 + if (compressedBlock.size() == 0) {
  575 + return read;
  576 + }
  577 + decompressedBlock = qUncompress(compressedBlock);
  578 +
  579 + decompressedBlockReader.close();
  580 + decompressedBlockReader.setBuffer(&decompressedBlock);
  581 + decompressedBlockReader.open(QIODevice::ReadOnly);
  582 + }
  583 + }
  584 + return blockReader.atEnd() && !basis->isReadable() ? -1 : read;
  585 +}
  586 +
  587 +bool BlockCompression::isSequential() const
  588 +{
  589 + return true;
  590 +}
  591 +
  592 +qint64 BlockCompression::writeData(const char *data, qint64 remaining)
  593 +{
  594 + qint64 written = 0;
  595 +
  596 + while (remaining > 0) {
  597 + // how much more can be put in this buffer?
  598 + qint64 capacity = blockSize - precompressedBlockWriter->pos();
  599 +
  600 + // don't try to write beyond capacity
  601 + qint64 write_size = qMin(capacity, remaining);
  602 +
  603 + qint64 singleWrite = precompressedBlockWriter->write(data, write_size);
  604 + // ignore the error case here, we consdier basis's failure mode the real
  605 + // end case
  606 + if (singleWrite == -1)
  607 + singleWrite = 0;
  608 +
  609 + remaining -= singleWrite;
  610 + data += singleWrite;
  611 + written += singleWrite;
  612 +
  613 + if (remaining > 0) {
  614 + QByteArray compressedBlock = qCompress(precompressedBlockWriter->buffer(), -1);
  615 +
  616 + if (compressedBlock.size() != 0)
  617 + blockWriter << compressedBlock;
  618 +
  619 + delete precompressedBlockWriter;
  620 + precompressedBlockWriter = new QBuffer;
  621 + precompressedBlockWriter->open(QIODevice::ReadWrite);
  622 + }
  623 + }
  624 + return basis->isWritable() ? written : -1;
  625 +}
  626 +
  627 +
503 628
504 } // namespace QtUtils 629 } // namespace QtUtils
505 630
openbr/core/qtutils.h
@@ -17,6 +17,7 @@ @@ -17,6 +17,7 @@
17 #ifndef QTUTILS_QTUTILS_H 17 #ifndef QTUTILS_QTUTILS_H
18 #define QTUTILS_QTUTILS_H 18 #define QTUTILS_QTUTILS_H
19 19
  20 +#include <QBuffer>
20 #include <QByteArray> 21 #include <QByteArray>
21 #include <QDir> 22 #include <QDir>
22 #include <QFile> 23 #include <QFile>
@@ -93,6 +94,38 @@ namespace QtUtils @@ -93,6 +94,38 @@ namespace QtUtils
93 94
94 /**** Rect Utilities ****/ 95 /**** Rect Utilities ****/
95 float overlap(const QRectF &r, const QRectF &s); 96 float overlap(const QRectF &r, const QRectF &s);
  97 +
  98 +
  99 + class BlockCompression : public QIODevice
  100 + {
  101 + public:
  102 + BlockCompression(QIODevice *_basis);
  103 + BlockCompression();
  104 + int blockSize;
  105 + QIODevice *basis;
  106 +
  107 + bool open(QIODevice::OpenMode mode);
  108 +
  109 + void close();
  110 +
  111 + void setBasis(QIODevice *_basis);
  112 +
  113 + QDataStream blockReader;
  114 + QByteArray decompressedBlock;
  115 + QBuffer decompressedBlockReader;
  116 +
  117 + // read from current decompressed block, if out of space, read and decompress another
  118 + // block from basis
  119 + qint64 readData(char *data, qint64 remaining);
  120 +
  121 + bool isSequential() const;
  122 +
  123 + // write to a QByteArray, when max block sized is reached, compress and write
  124 + // it to basis
  125 + QBuffer * precompressedBlockWriter;
  126 + QDataStream blockWriter;
  127 + qint64 writeData(const char *data, qint64 remaining);
  128 + };
96 } 129 }
97 130
98 #endif // QTUTILS_QTUTILS_H 131 #endif // QTUTILS_QTUTILS_H
openbr/plugins/algorithms.cpp
@@ -31,15 +31,15 @@ class AlgorithmsInitializer : public Initializer @@ -31,15 +31,15 @@ class AlgorithmsInitializer : public Initializer
31 void initialize() const 31 void initialize() const
32 { 32 {
33 // Face 33 // Face
34 - Globals->abbreviations.insert("FaceRecognition", "FaceDetection+Expand+<FaceRecognitionRegistration>+Expand+<FaceRecognitionExtraction>+<FaceRecognitionEmbedding>+<FaceRecognitionQuantization>+SetMetadata(AlgorithmID,-1):MatchProbability(ByteL1)");  
35 - Globals->abbreviations.insert("GenderClassification", "FaceDetection+Expand+<FaceClassificationRegistration>+Expand+<FaceClassificationExtraction>+<GenderClassifier>+Discard");  
36 - Globals->abbreviations.insert("AgeRegression", "FaceDetection+Expand+<FaceClassificationRegistration>+Expand+<FaceClassificationExtraction>+<AgeRegressor>+Discard"); 34 + Globals->abbreviations.insert("FaceRecognition", "FaceDetection+FaceRecognitionRegistration+<FaceRecognitionExtraction>+<FaceRecognitionEmbedding>+<FaceRecognitionQuantization>+SetMetadata(AlgorithmID,-1):Unit(ByteL1)");
  35 + Globals->abbreviations.insert("GenderClassification", "FaceDetection+Expand+FaceClassificationRegistration+Expand+<FaceClassificationExtraction>+<GenderClassifier>+Discard");
  36 + Globals->abbreviations.insert("AgeRegression", "FaceDetection+Expand+FaceClassificationRegistration+Expand+<FaceClassificationExtraction>+<AgeRegressor>+Discard");
37 Globals->abbreviations.insert("FaceQuality", "Open+Expand+Cascade(FrontalFace)+ASEFEyes+Affine(64,64,0.25,0.35)+ImageQuality+Cvt(Gray)+DFFS+Discard"); 37 Globals->abbreviations.insert("FaceQuality", "Open+Expand+Cascade(FrontalFace)+ASEFEyes+Affine(64,64,0.25,0.35)+ImageQuality+Cvt(Gray)+DFFS+Discard");
38 Globals->abbreviations.insert("MedianFace", "Open+Expand+Cascade(FrontalFace)+ASEFEyes+Affine(256,256,0.37,0.45)+Center(Median)"); 38 Globals->abbreviations.insert("MedianFace", "Open+Expand+Cascade(FrontalFace)+ASEFEyes+Affine(256,256,0.37,0.45)+Center(Median)");
39 Globals->abbreviations.insert("BlurredFaceDetection", "Open+LimitSize(1024)+SkinMask/(Cvt(Gray)+GradientMask)+And+Morph(Erode,16)+LargestConvexArea"); 39 Globals->abbreviations.insert("BlurredFaceDetection", "Open+LimitSize(1024)+SkinMask/(Cvt(Gray)+GradientMask)+And+Morph(Erode,16)+LargestConvexArea");
40 Globals->abbreviations.insert("DrawFaceDetection", "Open+Cascade(FrontalFace)+Expand+ASEFEyes+Draw(inPlace=true)"); 40 Globals->abbreviations.insert("DrawFaceDetection", "Open+Cascade(FrontalFace)+Expand+ASEFEyes+Draw(inPlace=true)");
41 Globals->abbreviations.insert("ShowFaceDetection", "DrawFaceDetection+Contract+First+Show+Discard"); 41 Globals->abbreviations.insert("ShowFaceDetection", "DrawFaceDetection+Contract+First+Show+Discard");
42 - Globals->abbreviations.insert("DownloadFaceRecognition", "Download+Open+ROI+Expand+Cvt(Gray)+Cascade(FrontalFace)+Expand+<FaceRecognitionRegistration>+Expand+<FaceRecognitionExtraction>+<FaceRecognitionEmbedding>+<FaceRecognitionQuantization>+SetMetadata(AlgorithmID,-1):MatchProbability(ByteL1)"); 42 + Globals->abbreviations.insert("DownloadFaceRecognition", "Download+Open+ROI+Cvt(Gray)+Cascade(FrontalFace)+FaceRecognitionRegistration+<FaceRecognitionExtraction>+<FaceRecognitionEmbedding>+<FaceRecognitionQuantization>+SetMetadata(AlgorithmID,-1):Unit(ByteL1)");
43 Globals->abbreviations.insert("OpenBR", "FaceRecognition"); 43 Globals->abbreviations.insert("OpenBR", "FaceRecognition");
44 Globals->abbreviations.insert("GenderEstimation", "GenderClassification"); 44 Globals->abbreviations.insert("GenderEstimation", "GenderClassification");
45 Globals->abbreviations.insert("AgeEstimation", "AgeRegression"); 45 Globals->abbreviations.insert("AgeEstimation", "AgeRegression");
@@ -50,7 +50,7 @@ class AlgorithmsInitializer : public Initializer @@ -50,7 +50,7 @@ class AlgorithmsInitializer : public Initializer
50 // Video 50 // Video
51 Globals->abbreviations.insert("DisplayVideo", "FPSLimit(30)+Show(false,[FrameNumber])+Discard"); 51 Globals->abbreviations.insert("DisplayVideo", "FPSLimit(30)+Show(false,[FrameNumber])+Discard");
52 Globals->abbreviations.insert("PerFrameDetection", "SaveMat(original)+Cvt(Gray)+Cascade(FrontalFace)+ASEFEyes+RestoreMat(original)+Draw(inPlace=true)+Show(false,[FrameNumber])+Discard"); 52 Globals->abbreviations.insert("PerFrameDetection", "SaveMat(original)+Cvt(Gray)+Cascade(FrontalFace)+ASEFEyes+RestoreMat(original)+Draw(inPlace=true)+Show(false,[FrameNumber])+Discard");
53 - Globals->abbreviations.insert("AgeGenderDemo", "SaveMat(original)+Cvt(Gray)+Cascade(FrontalFace)+Expand+<FaceClassificationRegistration>+<FaceClassificationExtraction>+<AgeRegressor>/<GenderClassifier>+Discard+RestoreMat(original)+Draw(inPlace=true)+DrawPropertiesPoint([Age,Gender],Affine_0,inPlace=true)+SaveMat(original)+Discard+Contract+RestoreMat(original)+FPSCalc+Show(false,[AvgFPS,Age,Gender])+Discard"); 53 + Globals->abbreviations.insert("AgeGenderDemo", "SaveMat(original)+Cvt(Gray)+Cascade(FrontalFace)+Expand+FaceClassificationRegistration+<FaceClassificationExtraction>+<AgeRegressor>/<GenderClassifier>+Discard+RestoreMat(original)+Draw(inPlace=true)+DrawPropertiesPoint([Age,Gender],Affine_0,inPlace=true)+SaveMat(original)+Discard+Contract+RestoreMat(original)+FPSCalc+Show(false,[AvgFPS,Age,Gender])+Discard");
54 Globals->abbreviations.insert("ShowOpticalFlowField", "SaveMat(original)+AggregateFrames(2)+OpticalFlow(useMagnitude=false)+Grid(100,100)+DrawOpticalFlow+FPSLimit(30)+Show(false)+Discard"); 54 Globals->abbreviations.insert("ShowOpticalFlowField", "SaveMat(original)+AggregateFrames(2)+OpticalFlow(useMagnitude=false)+Grid(100,100)+DrawOpticalFlow+FPSLimit(30)+Show(false)+Discard");
55 Globals->abbreviations.insert("ShowOpticalFlowMagnitude", "AggregateFrames(2)+OpticalFlow+Normalize(Range,false,0,255)+Cvt(Color)+Draw+FPSLimit(30)+Show(false)+Discard"); 55 Globals->abbreviations.insert("ShowOpticalFlowMagnitude", "AggregateFrames(2)+OpticalFlow+Normalize(Range,false,0,255)+Cvt(Color)+Draw+FPSLimit(30)+Show(false)+Discard");
56 Globals->abbreviations.insert("ShowMotionSegmentation", "DropFrames(5)+AggregateFrames(2)+OpticalFlow+CvtUChar+WatershedSegmentation+DrawSegmentation+Draw+FPSLimit(30)+Show(false)+Discard"); 56 Globals->abbreviations.insert("ShowMotionSegmentation", "DropFrames(5)+AggregateFrames(2)+OpticalFlow+CvtUChar+WatershedSegmentation+DrawSegmentation+Draw+FPSLimit(30)+Show(false)+Discard");
@@ -92,11 +92,11 @@ class AlgorithmsInitializer : public Initializer @@ -92,11 +92,11 @@ class AlgorithmsInitializer : public Initializer
92 Globals->abbreviations.insert("DenseHOG", "Gradient+RectRegions(8,8,6,6)+Bin(0,360,8)+Hist(8)"); 92 Globals->abbreviations.insert("DenseHOG", "Gradient+RectRegions(8,8,6,6)+Bin(0,360,8)+Hist(8)");
93 Globals->abbreviations.insert("DenseSIFT", "(Grid(10,10)+SIFTDescriptor(12)+ByRow)"); 93 Globals->abbreviations.insert("DenseSIFT", "(Grid(10,10)+SIFTDescriptor(12)+ByRow)");
94 Globals->abbreviations.insert("DenseSIFT2", "(Grid(5,5)+SIFTDescriptor(12)+ByRow)"); 94 Globals->abbreviations.insert("DenseSIFT2", "(Grid(5,5)+SIFTDescriptor(12)+ByRow)");
95 - Globals->abbreviations.insert("FaceRecognitionRegistration", "(ASEFEyes+Affine(88,88,0.25,0.35)+DownsampleTraining(FTE(DFFS),instances=1))"); 95 + Globals->abbreviations.insert("FaceRecognitionRegistration", "ASEFEyes+Affine(88,88,0.25,0.35)");
96 Globals->abbreviations.insert("FaceRecognitionExtraction", "(Mask+DenseSIFT/DenseLBP+DownsampleTraining(PCA(0.95),instances=1)+Normalize(L2)+Cat)"); 96 Globals->abbreviations.insert("FaceRecognitionExtraction", "(Mask+DenseSIFT/DenseLBP+DownsampleTraining(PCA(0.95),instances=1)+Normalize(L2)+Cat)");
97 Globals->abbreviations.insert("FaceRecognitionEmbedding", "(Dup(12)+RndSubspace(0.05,1)+DownsampleTraining(LDA(0.98),instances=-2)+Cat+DownsampleTraining(PCA(768),instances=1))"); 97 Globals->abbreviations.insert("FaceRecognitionEmbedding", "(Dup(12)+RndSubspace(0.05,1)+DownsampleTraining(LDA(0.98),instances=-2)+Cat+DownsampleTraining(PCA(768),instances=1))");
98 Globals->abbreviations.insert("FaceRecognitionQuantization", "(Normalize(L1)+Quantize)"); 98 Globals->abbreviations.insert("FaceRecognitionQuantization", "(Normalize(L1)+Quantize)");
99 - Globals->abbreviations.insert("FaceClassificationRegistration", "(ASEFEyes+Affine(56,72,0.33,0.45)+FTE(DFFS))"); 99 + Globals->abbreviations.insert("FaceClassificationRegistration", "ASEFEyes+Affine(56,72,0.33,0.45)");
100 Globals->abbreviations.insert("FaceClassificationExtraction", "((Grid(7,7)+SIFTDescriptor(8)+ByRow)/DenseLBP+DownsampleTraining(PCA(0.95),instances=-1, inputVariable=Gender)+Cat)"); 100 Globals->abbreviations.insert("FaceClassificationExtraction", "((Grid(7,7)+SIFTDescriptor(8)+ByRow)/DenseLBP+DownsampleTraining(PCA(0.95),instances=-1, inputVariable=Gender)+Cat)");
101 Globals->abbreviations.insert("AgeRegressor", "DownsampleTraining(Center(Range),instances=-1, inputVariable=Age)+DownsampleTraining(SVM(RBF,EPS_SVR,inputVariable=Age),instances=100, inputVariable=Age)"); 101 Globals->abbreviations.insert("AgeRegressor", "DownsampleTraining(Center(Range),instances=-1, inputVariable=Age)+DownsampleTraining(SVM(RBF,EPS_SVR,inputVariable=Age),instances=100, inputVariable=Age)");
102 Globals->abbreviations.insert("GenderClassifier", "DownsampleTraining(Center(Range),instances=-1, inputVariable=Gender)+DownsampleTraining(SVM(RBF,C_SVC,inputVariable=Gender),instances=4000, inputVariable=Gender)"); 102 Globals->abbreviations.insert("GenderClassifier", "DownsampleTraining(Center(Range),instances=-1, inputVariable=Gender)+DownsampleTraining(SVM(RBF,C_SVC,inputVariable=Gender),instances=4000, inputVariable=Gender)");
openbr/plugins/cascade.cpp
@@ -252,6 +252,8 @@ class CascadeTransform : public MetaTransform @@ -252,6 +252,8 @@ class CascadeTransform : public MetaTransform
252 void init() 252 void init()
253 { 253 {
254 cascadeResource.setResourceMaker(new CascadeResourceMaker(model)); 254 cascadeResource.setResourceMaker(new CascadeResourceMaker(model));
  255 + if (model == "Ear" || model == "Eye" || model == "FrontalFace" || model == "ProfileFace")
  256 + this->trainable = false;
255 } 257 }
256 258
257 // Train transform 259 // Train transform
openbr/plugins/meta.cpp
@@ -17,6 +17,8 @@ @@ -17,6 +17,8 @@
17 #include <QFutureSynchronizer> 17 #include <QFutureSynchronizer>
18 #include <QRegularExpression> 18 #include <QRegularExpression>
19 #include <QtConcurrentRun> 19 #include <QtConcurrentRun>
  20 +#include <qbuffer.h>
  21 +
20 #include "openbr_internal.h" 22 #include "openbr_internal.h"
21 #include "openbr/core/common.h" 23 #include "openbr/core/common.h"
22 #include "openbr/core/opencvutils.h" 24 #include "openbr/core/opencvutils.h"
@@ -94,17 +96,15 @@ class PipeTransform : public CompositeTransform @@ -94,17 +96,15 @@ class PipeTransform : public CompositeTransform
94 96
95 int i = 0; 97 int i = 0;
96 while (i < transforms.size()) { 98 while (i < transforms.size()) {
97 - fprintf(stderr, "\n%s", qPrintable(transforms[i]->objectName()));  
98 -  
99 // Conditional statement covers likely case that first transform is untrainable 99 // Conditional statement covers likely case that first transform is untrainable
100 if (transforms[i]->trainable) { 100 if (transforms[i]->trainable) {
101 - fprintf(stderr, " training..."); 101 + qDebug() << "Training " << transforms[i]->description() << "\n...";
102 transforms[i]->train(dataLines); 102 transforms[i]->train(dataLines);
103 } 103 }
104 104
105 // if the transform is time varying, we can't project it in parallel 105 // if the transform is time varying, we can't project it in parallel
106 if (transforms[i]->timeVarying()) { 106 if (transforms[i]->timeVarying()) {
107 - fprintf(stderr, "\n%s projecting...", qPrintable(transforms[i]->objectName())); 107 + qDebug() << "Projecting " << transforms[i]->description() << "\n...";
108 for (int j=0; j < dataLines.size();j++) { 108 for (int j=0; j < dataLines.size();j++) {
109 TemplateList junk; 109 TemplateList junk;
110 splitFTEs(dataLines[j], junk); 110 splitFTEs(dataLines[j], junk);
@@ -130,7 +130,16 @@ class PipeTransform : public CompositeTransform @@ -130,7 +130,16 @@ class PipeTransform : public CompositeTransform
130 !transforms[nextTrainableTransform]->timeVarying()) 130 !transforms[nextTrainableTransform]->timeVarying())
131 nextTrainableTransform++; 131 nextTrainableTransform++;
132 132
133 - fprintf(stderr, " projecting..."); 133 + // No more trainable transforms? Don't need any more projects then
  134 + if (nextTrainableTransform == transforms.size())
  135 + break;
  136 +
  137 + fprintf(stderr, "Projecting %s", qPrintable(transforms[i]->description()));
  138 + for (int j=i+1; j < nextTrainableTransform; j++)
  139 + fprintf(stderr,"+%s", qPrintable(transforms[j]->description()));
  140 + fprintf(stderr, "\n...\n");
  141 + fflush(stderr);
  142 +
134 QFutureSynchronizer<void> futures; 143 QFutureSynchronizer<void> futures;
135 for (int j=0; j < dataLines.size(); j++) 144 for (int j=0; j < dataLines.size(); j++)
136 futures.addFuture(QtConcurrent::run(this, &PipeTransform::_projectPartial, &dataLines[j], i, nextTrainableTransform)); 145 futures.addFuture(QtConcurrent::run(this, &PipeTransform::_projectPartial, &dataLines[j], i, nextTrainableTransform));
@@ -510,7 +519,6 @@ class LoadStoreTransform : public MetaTransform @@ -510,7 +519,6 @@ class LoadStoreTransform : public MetaTransform
510 519
511 public: 520 public:
512 Transform *transform; 521 Transform *transform;
513 - QString baseName;  
514 522
515 LoadStoreTransform() : transform(NULL) {} 523 LoadStoreTransform() : transform(NULL) {}
516 524
@@ -540,8 +548,8 @@ private: @@ -540,8 +548,8 @@ private:
540 void init() 548 void init()
541 { 549 {
542 if (transform != NULL) return; 550 if (transform != NULL) return;
543 - if (fileName.isEmpty()) baseName = QRegExp("^[_a-zA-Z0-9]+$").exactMatch(transformString) ? transformString : QtUtils::shortTextHash(transformString);  
544 - else baseName = fileName; 551 + if (fileName.isEmpty()) fileName = QRegExp("^[_a-zA-Z0-9]+$").exactMatch(transformString) ? transformString : QtUtils::shortTextHash(transformString);
  552 +
545 if (!tryLoad()) 553 if (!tryLoad())
546 transform = make(transformString); 554 transform = make(transformString);
547 else 555 else
@@ -553,19 +561,28 @@ private: @@ -553,19 +561,28 @@ private:
553 return transform->timeVarying(); 561 return transform->timeVarying();
554 } 562 }
555 563
556 - void train(const TemplateList &data) 564 + void train(const QList<TemplateList> &data)
557 { 565 {
558 if (QFileInfo(getFileName()).exists()) 566 if (QFileInfo(getFileName()).exists())
559 return; 567 return;
560 568
561 transform->train(data); 569 transform->train(data);
562 570
563 - qDebug("Storing %s", qPrintable(baseName));  
564 - QByteArray byteArray;  
565 - QDataStream stream(&byteArray, QFile::WriteOnly);  
566 - stream << transform->description(); 571 + qDebug("Storing %s", qPrintable(fileName));
  572 + QtUtils::BlockCompression compressedOut;
  573 + QFile fout(fileName);
  574 + QtUtils::touchDir(fout);
  575 + compressedOut.setBasis(&fout);
  576 +
  577 + QDataStream stream(&compressedOut);
  578 + QString desc = transform->description();
  579 +
  580 + if (!compressedOut.open(QFile::WriteOnly))
  581 + qFatal("Failed to open %s for writing.", qPrintable(file));
  582 +
  583 + stream << desc;
567 transform->store(stream); 584 transform->store(stream);
568 - QtUtils::writeFile(baseName, byteArray, -1); 585 + compressedOut.close();
569 } 586 }
570 587
571 void project(const Template &src, Template &dst) const 588 void project(const Template &src, Template &dst) const
@@ -595,8 +612,8 @@ private: @@ -595,8 +612,8 @@ private:
595 612
596 QString getFileName() const 613 QString getFileName() const
597 { 614 {
598 - if (QFileInfo(baseName).exists()) return baseName;  
599 - const QString file = Globals->sdkPath + "/share/openbr/models/transforms/" + baseName; 615 + if (QFileInfo(fileName).exists()) return fileName;
  616 + const QString file = Globals->sdkPath + "/share/openbr/models/transforms/" + fileName;
600 return QFileInfo(file).exists() ? file : QString(); 617 return QFileInfo(file).exists() ? file : QString();
601 } 618 }
602 619
@@ -606,12 +623,19 @@ private: @@ -606,12 +623,19 @@ private:
606 if (file.isEmpty()) return false; 623 if (file.isEmpty()) return false;
607 624
608 qDebug("Loading %s", qPrintable(file)); 625 qDebug("Loading %s", qPrintable(file));
609 - QByteArray data;  
610 - QtUtils::readFile(file, data, true);  
611 - QDataStream stream(&data, QFile::ReadOnly); 626 + QFile fin(file);
  627 + QtUtils::BlockCompression reader(&fin);
  628 + if (!reader.open(QIODevice::ReadOnly)) {
  629 + if (QFileInfo(file).exists()) qFatal("Unable to open %s for reading. Check file permissions.", qPrintable(file));
  630 + else qFatal("Unable to open %s for reading. File does not exist.", qPrintable(file));
  631 + }
  632 +
  633 + QDataStream stream(&reader);
612 stream >> transformString; 634 stream >> transformString;
  635 +
613 transform = Transform::make(transformString); 636 transform = Transform::make(transformString);
614 transform->load(stream); 637 transform->load(stream);
  638 +
615 return true; 639 return true;
616 } 640 }
617 }; 641 };
openbr/plugins/quality.cpp
@@ -77,6 +77,12 @@ class ImpostorUniquenessMeasureTransform : public Transform @@ -77,6 +77,12 @@ class ImpostorUniquenessMeasureTransform : public Transform
77 77
78 BR_REGISTER(Transform, ImpostorUniquenessMeasureTransform) 78 BR_REGISTER(Transform, ImpostorUniquenessMeasureTransform)
79 79
  80 +
  81 +float KDEPointer(const QList<float> *scores, double x, double h)
  82 +{
  83 + return Common::KernelDensityEstimation(*scores, x, h);
  84 +}
  85 +
80 /* Kernel Density Estimator */ 86 /* Kernel Density Estimator */
81 struct KDE 87 struct KDE
82 { 88 {
@@ -85,20 +91,35 @@ struct KDE @@ -85,20 +91,35 @@ struct KDE
85 QList<float> bins; 91 QList<float> bins;
86 92
87 KDE() : min(0), max(1), mean(0), stddev(1) {} 93 KDE() : min(0), max(1), mean(0), stddev(1) {}
88 - KDE(const QList<float> &scores) 94 +
  95 + KDE(const QList<float> &scores, bool trainKDE)
89 { 96 {
90 Common::MinMax(scores, &min, &max); 97 Common::MinMax(scores, &min, &max);
91 Common::MeanStdDev(scores, &mean, &stddev); 98 Common::MeanStdDev(scores, &mean, &stddev);
  99 +
  100 + if (!trainKDE)
  101 + return;
  102 +
92 double h = Common::KernelDensityBandwidth(scores); 103 double h = Common::KernelDensityBandwidth(scores);
93 const int size = 255; 104 const int size = 255;
94 bins.reserve(size); 105 bins.reserve(size);
95 - for (int i=0; i<size; i++)  
96 - bins.append(Common::KernelDensityEstimation(scores, min + (max-min)*i/(size-1), h)); 106 +
  107 + QFutureSynchronizer<float> futures;
  108 +
  109 + for (int i=0; i < size; i++)
  110 + futures.addFuture(QtConcurrent::run(KDEPointer, &scores, min + (max-min)*i/(size-1), h));
  111 + futures.waitForFinished();
  112 +
  113 + foreach(const QFuture<float> & future, futures.futures())
  114 + bins.append(future.result());
97 } 115 }
98 116
99 float operator()(float score, bool gaussian = true) const 117 float operator()(float score, bool gaussian = true) const
100 { 118 {
101 if (gaussian) return 1/(stddev*sqrt(2*CV_PI))*exp(-0.5*pow((score-mean)/stddev, 2)); 119 if (gaussian) return 1/(stddev*sqrt(2*CV_PI))*exp(-0.5*pow((score-mean)/stddev, 2));
  120 + if (bins.empty())
  121 + return -std::numeric_limits<float>::max();
  122 +
102 if (score <= min) return bins.first(); 123 if (score <= min) return bins.first();
103 if (score >= max) return bins.last(); 124 if (score >= max) return bins.last();
104 const float x = (score-min)/(max-min)*bins.size(); 125 const float x = (score-min)/(max-min)*bins.size();
@@ -123,8 +144,8 @@ struct MP @@ -123,8 +144,8 @@ struct MP
123 { 144 {
124 KDE genuine, impostor; 145 KDE genuine, impostor;
125 MP() {} 146 MP() {}
126 - MP(const QList<float> &genuineScores, const QList<float> &impostorScores)  
127 - : genuine(genuineScores), impostor(impostorScores) {} 147 + MP(const QList<float> &genuineScores, const QList<float> &impostorScores, bool trainKDE)
  148 + : genuine(genuineScores, trainKDE), impostor(impostorScores, trainKDE) {}
128 float operator()(float score, bool gaussian = true) const 149 float operator()(float score, bool gaussian = true) const
129 { 150 {
130 const float g = genuine(score, gaussian); 151 const float g = genuine(score, gaussian);
@@ -165,7 +186,7 @@ class MatchProbabilityDistance : public Distance @@ -165,7 +186,7 @@ class MatchProbabilityDistance : public Distance
165 const QList<int> labels = src.indexProperty(inputVariable); 186 const QList<int> labels = src.indexProperty(inputVariable);
166 QScopedPointer<MatrixOutput> matrixOutput(MatrixOutput::make(FileList(src.size()), FileList(src.size()))); 187 QScopedPointer<MatrixOutput> matrixOutput(MatrixOutput::make(FileList(src.size()), FileList(src.size())));
167 distance->compare(src, src, matrixOutput.data()); 188 distance->compare(src, src, matrixOutput.data());
168 - 189 +
169 QList<float> genuineScores, impostorScores; 190 QList<float> genuineScores, impostorScores;
170 genuineScores.reserve(labels.size()); 191 genuineScores.reserve(labels.size());
171 impostorScores.reserve(labels.size()*labels.size()); 192 impostorScores.reserve(labels.size()*labels.size());
@@ -178,8 +199,8 @@ class MatchProbabilityDistance : public Distance @@ -178,8 +199,8 @@ class MatchProbabilityDistance : public Distance
178 else impostorScores.append(score); 199 else impostorScores.append(score);
179 } 200 }
180 } 201 }
181 -  
182 - mp = MP(genuineScores, impostorScores); 202 +
  203 + mp = MP(genuineScores, impostorScores, !gaussian);
183 } 204 }
184 205
185 float compare(const Template &target, const Template &query) const 206 float compare(const Template &target, const Template &query) const
1 -Subproject commit 79938fe401faafead086b4711dd0b8f898a7a21e 1 +Subproject commit 85842e6da7738e317b9d40d5a395b92cd7b996e1