Commit 447ef443d15967d13129cf07ea40db9f8db87b5a

Authored by Jordan Cheney
1 parent edcbc8b8

More steps towards representations and proper formatting in backend

openbr/core/boost.cpp
... ... @@ -1158,6 +1158,7 @@ float CascadeBoost::predict( int sampleIdx, bool returnSum ) const
1158 1158 {
1159 1159 CV_Assert( weak );
1160 1160 double sum = 0;
  1161 +
1161 1162 CvSeqReader reader;
1162 1163 cvStartReadSeq( weak, &reader );
1163 1164 cvSetSeqReaderPos( &reader, 0 );
... ... @@ -1167,6 +1168,7 @@ float CascadeBoost::predict( int sampleIdx, bool returnSum ) const
1167 1168 CV_READ_SEQ_ELEM( wtree, reader );
1168 1169 sum += ((CascadeBoostTree*)wtree)->predict(sampleIdx)->value;
1169 1170 }
  1171 +
1170 1172 if( !returnSum )
1171 1173 sum = sum < threshold - CV_THRESHOLD_EPS ? 0.0 : 1.0;
1172 1174 return (float)sum;
... ...
openbr/core/cascade.cpp
... ... @@ -130,10 +130,12 @@ bool BrCascadeClassifier::train(const string _cascadeDirName,
130 130 numStages = _numStages;
131 131 imgReader.create(_posImages, _negImages, winSize);
132 132  
  133 + Representation *representation = Representation::make("MBLBP(24,24)", NULL);
  134 +
133 135 stageParams = new CascadeBoostParams;
134 136 *stageParams = _stageParams;
135 137 featureEvaluator = new FeatureEvaluator;
136   - featureEvaluator->init(numPos + numNeg, winSize);
  138 + featureEvaluator->init(representation, numPos + numNeg);
137 139 stageClassifiers.reserve( numStages );
138 140  
139 141 double requiredLeafFARate = pow( (double) stageParams->maxFalseAlarm, (double) numStages ) /
... ...
openbr/core/features.cpp
... ... @@ -5,65 +5,22 @@ using namespace br;
5 5  
6 6 //------------------------------------- FeatureEvaluator ---------------------------------------
7 7  
8   -void FeatureEvaluator::init(int _maxSampleCount, Size _winSize )
  8 +void FeatureEvaluator::init(Representation *_representation, int _maxSampleCount)
9 9 {
10   - CV_Assert(_maxSampleCount > 0);
11   - winSize = _winSize;
12   - numFeatures = 0;
13   - data.create((int)_maxSampleCount, (_winSize.width + 1) * (_winSize.height + 1), CV_32SC1);
  10 + representation = _representation;
  11 + data.create((int)_maxSampleCount, representation->postWindowSize().area(), CV_32SC1);
14 12 cls.create( (int)_maxSampleCount, 1, CV_32FC1 );
15   -
16   - maxCatCount = 256;
17   - featSize = 1;
18   -
19   - generateFeatures();
20 13 }
21 14  
22 15 void FeatureEvaluator::setImage(const Mat &img, uchar clsLabel, int idx)
23 16 {
24   - CV_Assert(img.cols == winSize.width);
25   - CV_Assert(img.rows == winSize.height);
26   - CV_Assert(idx < cls.rows);
27 17 cls.ptr<float>(idx)[0] = clsLabel;
28   - Mat integralImg(winSize.height + 1, winSize.width + 1, data.type(), data.ptr<int>(idx));
29   - integral(img, integralImg);
30   -}
31   -
32   -void FeatureEvaluator::writeFeatures(FileStorage &fs, const Mat &featureMap) const
33   -{
34   - _writeFeatures( features, fs, featureMap );
35   -}
36 18  
37   -void FeatureEvaluator::generateFeatures()
38   -{
39   - int offset = winSize.width + 1;
40   - for (int x = 0; x < winSize.width; x++)
41   - for (int y = 0; y < winSize.height; y++)
42   - for (int w = 1; w <= winSize.width / 3; w++)
43   - for (int h = 1; h <= winSize.height / 3; h++)
44   - if ((x+3*w <= winSize.width) && (y+3*h <= winSize.height))
45   - features.push_back(Feature(offset, x, y, w, h ));
46   - numFeatures = (int)features.size();
  19 + Mat integralImg(representation->postWindowSize(), data.type(), data.ptr<int>(idx));
  20 + representation->preprocess(img, integralImg);
47 21 }
48 22  
49   -FeatureEvaluator::Feature::Feature()
50   -{
51   - rect = cvRect(0, 0, 0, 0);
52   -}
53   -
54   -FeatureEvaluator::Feature::Feature( int offset, int x, int y, int _blockWidth, int _blockHeight )
55   -{
56   - Rect tr = rect = cvRect(x, y, _blockWidth, _blockHeight);
57   - CV_SUM_OFFSETS( p[0], p[1], p[4], p[5], tr, offset )
58   - tr.x += 2*rect.width;
59   - CV_SUM_OFFSETS( p[2], p[3], p[6], p[7], tr, offset )
60   - tr.y +=2*rect.height;
61   - CV_SUM_OFFSETS( p[10], p[11], p[14], p[15], tr, offset )
62   - tr.x -= 2*rect.width;
63   - CV_SUM_OFFSETS( p[8], p[9], p[12], p[13], tr, offset )
64   -}
65   -
66   -void FeatureEvaluator::Feature::write(FileStorage &fs) const
  23 +void FeatureEvaluator::writeFeatures(FileStorage &fs, const Mat &featureMap) const
67 24 {
68   - fs << CC_RECT << "[:" << rect.x << rect.y << rect.width << rect.height << "]";
  25 + representation->write(fs, featureMap);
69 26 }
... ...
openbr/core/features.h
... ... @@ -48,90 +48,28 @@
48 48 #define TIME( arg ) (time( arg ))
49 49 #endif
50 50  
51   -#define CV_SUM_OFFSETS( p0, p1, p2, p3, rect, step ) \
52   - /* (x, y) */ \
53   - (p0) = (rect).x + (step) * (rect).y; \
54   - /* (x + w, y) */ \
55   - (p1) = (rect).x + (rect).width + (step) * (rect).y; \
56   - /* (x + w, y) */ \
57   - (p2) = (rect).x + (step) * ((rect).y + (rect).height); \
58   - /* (x + w, y + h) */ \
59   - (p3) = (rect).x + (rect).width + (step) * ((rect).y + (rect).height);
60   -
61 51 namespace br
62 52 {
63 53  
64   -template<class Feature>
65   -void _writeFeatures( const std::vector<Feature> features, cv::FileStorage &fs, const cv::Mat& featureMap )
66   -{
67   - fs << CC_FEATURES << "[";
68   - const cv::Mat_<int>& featureMap_ = (const cv::Mat_<int>&)featureMap;
69   - for ( int fi = 0; fi < featureMap.cols; fi++ )
70   - if ( featureMap_(0, fi) >= 0 )
71   - {
72   - fs << "{";
73   - features[fi].write( fs );
74   - fs << "}";
75   - }
76   - fs << "]";
77   -}
78   -
79 54 class FeatureEvaluator
80 55 {
81 56 public:
82 57 ~FeatureEvaluator() {}
83   - void init(int _maxSampleCount, cv::Size _winSize);
  58 + void init(Representation *_representation, int _maxSampleCount);
84 59 void setImage(const cv::Mat& img, uchar clsLabel, int idx);
85 60 void writeFeatures(cv::FileStorage &fs, const cv::Mat& featureMap) const;
86   - float operator()(int featureIdx, int sampleIdx) const { return (float)features[featureIdx].calc(data, sampleIdx); }
  61 + float operator()(int featureIdx, int sampleIdx) const { return representation->evaluate(data.row(sampleIdx), featureIdx); }
87 62  
88   - int getNumFeatures() const { return numFeatures; }
89   - int getMaxCatCount() const { return maxCatCount; }
90   - int getFeatureSize() const { return featSize; }
  63 + int getNumFeatures() const { return representation->numFeatures(); }
  64 + int getMaxCatCount() const { return representation->maxCatCount(); }
  65 + int getFeatureSize() const { return 1; }
91 66 const cv::Mat& getCls() const { return cls; }
92 67 float getCls(int si) const { return cls.at<float>(si, 0); }
93 68  
94   -protected:
95   - void generateFeatures();
96   -
97   - class Feature
98   - {
99   - public:
100   - Feature();
101   - Feature( int offset, int x, int y, int _block_w, int _block_h );
102   - uchar calc( const cv::Mat &data, int y ) const;
103   - void write( cv::FileStorage &fs ) const;
104   -
105   - cv::Rect rect;
106   - int p[16];
107   - };
108   - std::vector<Feature> features;
109   -
110 69 cv::Mat data, cls;
111   -
112   - int npos, nneg;
113   - int numFeatures;
114   - int maxCatCount; // 0 in case of numerical features
115   - int featSize; // 1 in case of simple features (HAAR, LBP) and N_BINS(9)*N_CELLS(4) in case of Dalal's HOG features
116   -
117   - cv::Size winSize;
  70 + Representation *representation;
118 71 };
119 72  
120   -inline uchar FeatureEvaluator::Feature::calc(const cv::Mat &data, int y) const
121   -{
122   - const int* ptr = data.ptr<int>(y);
123   - int cval = ptr[p[5]] - ptr[p[6]] - ptr[p[9]] + ptr[p[10]];
124   -
125   - return (uchar)((ptr[p[0]] - ptr[p[1]] - ptr[p[4]] + ptr[p[5]] >= cval ? 128 : 0) | // 0
126   - (ptr[p[1]] - ptr[p[2]] - ptr[p[5]] + ptr[p[6]] >= cval ? 64 : 0) | // 1
127   - (ptr[p[2]] - ptr[p[3]] - ptr[p[6]] + ptr[p[7]] >= cval ? 32 : 0) | // 2
128   - (ptr[p[6]] - ptr[p[7]] - ptr[p[10]] + ptr[p[11]] >= cval ? 16 : 0) | // 5
129   - (ptr[p[10]] - ptr[p[11]] - ptr[p[14]] + ptr[p[15]] >= cval ? 8 : 0) | // 8
130   - (ptr[p[9]] - ptr[p[10]] - ptr[p[13]] + ptr[p[14]] >= cval ? 4 : 0) | // 7
131   - (ptr[p[8]] - ptr[p[9]] - ptr[p[12]] + ptr[p[13]] >= cval ? 2 : 0) | // 6
132   - (ptr[p[4]] - ptr[p[5]] - ptr[p[8]] + ptr[p[9]] >= cval ? 1 : 0)); // 3
133   -}
134   -
135 73 } // namespace br
136 74  
137 75 #endif // FEATURE_H
... ...
openbr/openbr_plugin.h
... ... @@ -1395,18 +1395,27 @@ class BR_EXPORT Representation : public Object
1395 1395 {
1396 1396 Q_OBJECT
1397 1397  
  1398 + Q_PROPERTY(int winWidth READ get_winWidth WRITE set_winWidth RESET reset_winWidth STORED false)
  1399 + Q_PROPERTY(int winHeight READ get_winHeight WRITE set_winHeight RESET reset_winHeight STORED false)
  1400 + BR_PROPERTY(int, winWidth, 24)
  1401 + BR_PROPERTY(int, winHeight, 24)
  1402 +
1398 1403 public:
1399 1404 virtual ~Representation() {}
1400 1405  
1401 1406 static Representation *make(QString str, QObject *parent); /*!< \brief Make a representation from a string. */
1402   - virtual cv::Mat preprocess(const cv::Mat &image) const { return image; }
  1407 + virtual void preprocess(const cv::Mat &src, cv::Mat &dst) const { dst = src; }
1403 1408 virtual void train(const QList<cv::Mat> &images, const QList<float> &labels) { (void) images; (void)labels; }
  1409 +
  1410 + virtual float evaluate(const cv::Mat &image, int idx) const = 0;
1404 1411 // By convention, an empty indices list will result in all feature responses being calculated
1405 1412 // and returned.
1406 1413 virtual cv::Mat evaluate(const cv::Mat &image, const QList<int> &indices = QList<int>()) const = 0;
1407 1414  
  1415 + virtual cv::Size preWindowSize() const = 0; // window size before preprocessing
  1416 + virtual cv::Size postWindowSize() const = 0; // window size after preprocessing
1408 1417 virtual int numFeatures() const = 0;
1409   - virtual cv::Size windowSize() const = 0;
  1418 + virtual int maxCatCount() const = 0;
1410 1419  
1411 1420 // Temporary for OpenCV compatibility
1412 1421 virtual void write( cv::FileStorage &fs, const cv::Mat &featureMap ) { (void)fs; (void)featureMap; }
... ... @@ -1421,12 +1430,20 @@ public:
1421 1430  
1422 1431 static Classifier *make(QString str, QObject *parent); /*!< \brief Make a classifier from a string. */
1423 1432  
1424   - virtual Classifier *clone() const { return Factory<Classifier>::make("." + description(false)); }
1425   -
1426 1433 virtual void train(const QList<cv::Mat> &images, const QList<float> &labels) = 0;
1427 1434 // By convention, classify should return a value normalized such that the threshold is 0. Negative values
1428 1435 // can be interpreted as a negative classification and positive values as a positive classification.
1429 1436 virtual float classify(const cv::Mat &image) const = 0;
  1437 +
  1438 + // Slots for representation
  1439 + virtual cv::Size windowSize() const = 0;
  1440 +
  1441 + // OpenCV compatibility
  1442 + virtual int numFeatures() const = 0;
  1443 + virtual int maxCatCount() const = 0;
  1444 + virtual void getUsedFeatures(cv::Mat &featureMap) const { (void)featureMap; return; }
  1445 + virtual void write(cv::FileStorage &fs, const cv::Mat &featureMap) const { (void)fs; (void)featureMap; }
  1446 + virtual void writeFeatures(cv::FileStorage &fs, const cv::Mat &featureMap) const { (void)fs; (void)featureMap; }
1430 1447 };
1431 1448  
1432 1449 /*!
... ...
openbr/plugins/classification/boostedforest.cpp
... ... @@ -11,12 +11,14 @@ class BoostedForestClassifier : public Classifier
11 11 {
12 12 Q_OBJECT
13 13  
  14 + Q_PROPERTY(br::Representation *representation READ get_representation WRITE set_representation RESET reset_representation STORED false)
14 15 Q_PROPERTY(float minTAR READ get_minTAR WRITE set_minTAR RESET reset_minTAR STORED false)
15 16 Q_PROPERTY(float maxFAR READ get_maxFAR WRITE set_maxFAR RESET reset_maxFAR STORED false)
16 17 Q_PROPERTY(float trimRate READ get_trimRate WRITE set_trimRate RESET reset_trimRate STORED false)
17 18 Q_PROPERTY(int maxDepth READ get_maxDepth WRITE set_maxDepth RESET reset_maxDepth STORED false)
18 19 Q_PROPERTY(int maxWeakCount READ get_maxWeakCount WRITE set_maxWeakCount RESET reset_maxWeakCount STORED false)
19 20  
  21 + BR_PROPERTY(br::Representation *, representation, NULL)
20 22 BR_PROPERTY(float, minTAR, 0.995)
21 23 BR_PROPERTY(float, maxFAR, 0.5)
22 24 BR_PROPERTY(float, trimRate, 0.95)
... ... @@ -24,16 +26,56 @@ class BoostedForestClassifier : public Classifier
24 26 BR_PROPERTY(int, maxWeakCount, 100)
25 27  
26 28 CascadeBoost *boost;
  29 + FeatureEvaluator *featureEvaluator;
27 30  
28 31 void train(const QList<Mat> &images, const QList<float> &labels)
29 32 {
30   - (void)images; (void)labels;
  33 + CascadeBoostParams params(CvBoost::GENTLE, minTAR, maxFAR, trimRate, maxDepth, maxWeakCount);
  34 +
  35 + featureEvaluator = new FeatureEvaluator;
  36 + featureEvaluator->init(representation, images.size());
  37 +
  38 + for (int i = 0; i < images.size(); i++)
  39 + featureEvaluator->setImage(images[i], labels[i], i);
  40 +
  41 + boost = new CascadeBoost;
  42 + boost->train(featureEvaluator, images.size(), 1024, 1024, params);
31 43 }
32 44  
33 45 float classify(const Mat &image) const
34 46 {
35   - (void) image;
36   - return 0.;
  47 + featureEvaluator->setImage(image, 0, 0);
  48 + return boost->predict(0);
  49 + }
  50 +
  51 + int numFeatures() const
  52 + {
  53 + return representation->numFeatures();
  54 + }
  55 +
  56 + int maxCatCount() const
  57 + {
  58 + return representation->maxCatCount();
  59 + }
  60 +
  61 + Size windowSize() const
  62 + {
  63 + return representation->preWindowSize();
  64 + }
  65 +
  66 + void getUsedFeatures(Mat &featureMap) const
  67 + {
  68 + boost->markUsedFeaturesInMap(featureMap);
  69 + }
  70 +
  71 + void write(FileStorage &fs, const Mat &featureMap) const
  72 + {
  73 + boost->write(fs, featureMap);
  74 + }
  75 +
  76 + void writeFeatures(FileStorage &fs, const Mat &featureMap) const
  77 + {
  78 + featureEvaluator->writeFeatures(fs, featureMap);
37 79 }
38 80 };
39 81  
... ...
openbr/plugins/classification/cascade.cpp
1 1 #include <openbr/plugins/openbr_internal.h>
2   -#include <openbr/core/cascade.h>
  2 +#include <openbr/core/features.h>
  3 +#include <openbr/core/boost.h>
3 4  
4 5 using namespace cv;
5 6  
6 7 namespace br
7 8 {
8 9  
  10 +struct ImageHandler
  11 +{
  12 + bool create( const QList<cv::Mat> &_posImages, const QList<cv::Mat> &_negImages, cv::Size _winSize )
  13 + {
  14 + posImages = _posImages;
  15 + negImages = _negImages;
  16 + winSize = _winSize;
  17 +
  18 + posIdx = negIdx = 0;
  19 +
  20 + src.create( 0, 0 , CV_8UC1 );
  21 + img.create( 0, 0, CV_8UC1 );
  22 + point = offset = Point( 0, 0 );
  23 + scale = 1.0F;
  24 + scaleFactor = 1.4142135623730950488016887242097F;
  25 + stepFactor = 0.5F;
  26 + round = 0;
  27 +
  28 + return true;
  29 + }
  30 +
  31 + void restart() { posIdx = 0; }
  32 +
  33 + int numPos() const { return posImages.size(); }
  34 + int numNeg() const { return negImages.size(); }
  35 +
  36 + bool nextNeg()
  37 + {
  38 + Point _offset = Point(0,0);
  39 + size_t count = negImages.size();
  40 + for (size_t i = 0; i < count; i++) {
  41 + src = negImages[negIdx++];
  42 + if( src.empty() )
  43 + continue;
  44 + round += negIdx / count;
  45 + round = round % (winSize.width * winSize.height);
  46 + negIdx %= count;
  47 +
  48 + _offset.x = std::min( (int)round % winSize.width, src.cols - winSize.width );
  49 + _offset.y = std::min( (int)round / winSize.width, src.rows - winSize.height );
  50 + if( !src.empty() && src.type() == CV_8UC1 && _offset.x >= 0 && _offset.y >= 0 )
  51 + break;
  52 + }
  53 +
  54 + if( src.empty() )
  55 + return false; // no appropriate image
  56 + point = offset = _offset;
  57 + scale = max( ((float)winSize.width + point.x) / ((float)src.cols),
  58 + ((float)winSize.height + point.y) / ((float)src.rows) );
  59 +
  60 + Size sz( (int)(scale*src.cols + 0.5F), (int)(scale*src.rows + 0.5F) );
  61 + resize( src, img, sz );
  62 + return true;
  63 + }
  64 +
  65 + bool getNeg(cv::Mat &_img)
  66 + {
  67 + if( img.empty() )
  68 + if ( !nextNeg() )
  69 + return false;
  70 +
  71 + Mat mat( winSize.height, winSize.width, CV_8UC1,
  72 + (void*)(img.data + point.y * img.step + point.x * img.elemSize()), img.step );
  73 + mat.copyTo(_img);
  74 +
  75 + if( (int)( point.x + (1.0F + stepFactor ) * winSize.width ) < img.cols )
  76 + point.x += (int)(stepFactor * winSize.width);
  77 + else
  78 + {
  79 + point.x = offset.x;
  80 + if( (int)( point.y + (1.0F + stepFactor ) * winSize.height ) < img.rows )
  81 + point.y += (int)(stepFactor * winSize.height);
  82 + else
  83 + {
  84 + point.y = offset.y;
  85 + scale *= scaleFactor;
  86 + if( scale <= 1.0F )
  87 + resize( src, img, Size( (int)(scale*src.cols), (int)(scale*src.rows) ) );
  88 + else
  89 + {
  90 + if ( !nextNeg() )
  91 + return false;
  92 + }
  93 + }
  94 + }
  95 + return true;
  96 + }
  97 +
  98 + bool getPos(cv::Mat &_img)
  99 + {
  100 + if (posIdx >= posImages.size())
  101 + return false;
  102 +
  103 + posImages[posIdx++].copyTo(_img);
  104 + return true;
  105 + }
  106 +
  107 + QList<cv::Mat> posImages, negImages;
  108 +
  109 + int posIdx, negIdx;
  110 +
  111 + cv::Mat src, img;
  112 + cv::Point offset, point;
  113 + float scale;
  114 + float scaleFactor;
  115 + float stepFactor;
  116 + size_t round;
  117 + cv::Size winSize;
  118 +};
  119 +
9 120 class _CascadeClassifier : public Classifier
10 121 {
11 122 Q_OBJECT
12 123  
13   - Q_PROPERTY(br::Classifier *stage READ get_stage WRITE set_stage RESET reset_stage STORED false)
  124 + Q_PROPERTY(QString stageDescription READ get_stageDescription WRITE set_stageDescription RESET reset_stageDescription STORED false)
14 125 Q_PROPERTY(int numStages READ get_numStages WRITE set_numStages RESET reset_numStages STORED false)
  126 + Q_PROPERTY(int numPos READ get_numPos WRITE set_numPos RESET reset_numPos STORED false)
15 127 Q_PROPERTY(int numNegs READ get_numNegs WRITE set_numNegs RESET reset_numNegs STORED false)
16 128 Q_PROPERTY(float maxFAR READ get_maxFAR WRITE set_maxFAR RESET reset_maxFAR STORED false)
17 129  
18   - BR_PROPERTY(br::Classifier *, stage, NULL)
  130 + BR_PROPERTY(QString, stageDescription, "")
19 131 BR_PROPERTY(int, numStages, 20)
  132 + BR_PROPERTY(int, numPos, 1000)
20 133 BR_PROPERTY(int, numNegs, 1000)
21 134 BR_PROPERTY(float, maxFAR, pow(0.5, numStages))
22 135  
... ... @@ -28,127 +141,132 @@ class _CascadeClassifier : public Classifier
28 141 for (int i = 0; i < images.size(); i++)
29 142 labels[i] == 1 ? posImages.append(images[i]) : negImages.append(images[i]);
30 143  
31   - QList<Mat> trainingImages;
32   - QList<float> trainingLabels;
  144 + ImageHandler imgHandler;
  145 + imgHandler.create(posImages, negImages, Size(24, 24));
33 146  
  147 + stages.reserve(numStages);
34 148 for (int i = 0; i < numStages; i++) {
35   - float currFAR = updateTrainingSet(posImages, negImages, trainingImages, trainingLabels);
  149 + qDebug() << "===== TRAINING" << i << "stage =====";
  150 + qDebug() << "<BEGIN";
  151 +
  152 + QList<Mat> trainingImages;
  153 + QList<float> trainingLabels;
  154 +
  155 + float currFAR = fillTrainingSet(imgHandler, trainingImages, trainingLabels);
36 156  
37 157 if (currFAR < maxFAR) {
38 158 qDebug() << "FAR is below required level! Terminating early";
39 159 return;
40 160 }
41 161  
42   - Classifier *next_stage = stage->clone();
  162 + Classifier *next_stage = Classifier::make(stageDescription, NULL);
43 163 next_stage->train(trainingImages, trainingLabels);
44 164 stages.append(next_stage);
  165 +
  166 + qDebug() << "END>";
45 167 }
46 168 }
47 169  
48 170 float classify(const Mat &image) const
49 171 {
50   - (void) image;
51   - return 0.;
  172 + foreach (const Classifier *stage, stages)
  173 + if (stage->classify(image) == 0.0f)
  174 + return 0.0f;
  175 + return 1.0f;
52 176 }
53 177  
54   - float updateTrainingSet(const QList<Mat> &posImages, const QList<Mat> &negImages, QList<Mat> &trainingImages, QList<float> &trainingLabels)
  178 + int numFeatures() const
55 179 {
56   - trainingImages.clear();
57   - trainingLabels.clear();
  180 + return stages.first()->numFeatures();
  181 + }
58 182  
59   - foreach (const Mat &pos, posImages) {
60   - if (classify(pos) > 0) {
61   - trainingImages.append(pos);
62   - trainingLabels.append(1.);
63   - }
64   - }
  183 + int maxCatCount() const
  184 + {
  185 + return stages.first()->maxCatCount();
  186 + }
65 187  
66   - NegFinder finder(negImages, Size(24, 24));
67   - int totalNegs = 0, passedNegs = 0;
68   - while (true) {
69   - totalNegs++;
70   - Mat neg = finder.get();
71   - if (classify(neg) > 0) {
72   - trainingImages.append(neg);
73   - trainingLabels.append(0.);
74   - passedNegs++;
75   - }
  188 + cv::Size windowSize() const
  189 + {
  190 + return stages.first()->windowSize();
  191 + }
76 192  
77   - if (passedNegs >= numNegs)
78   - return passedNegs / (float)totalNegs;
79   - }
  193 + void getUsedFeatures(Mat &featureMap) const
  194 + {
  195 + foreach (const Classifier *stage, stages)
  196 + stage->getUsedFeatures(featureMap);
80 197 }
81 198  
82   -private:
83   - struct NegFinder
  199 + void write(FileStorage &fs, const Mat &featureMap) const
84 200 {
85   - NegFinder(const QList<Mat> &_negs, Size _winSize)
86   - {
87   - negs = _negs;
88   - winSize = _winSize;
89   -
90   - negIdx = round = 0;
91   - img.create( 0, 0, CV_8UC1 );
92   - point = offset = Point( 0, 0 );
93   - scale = 1.0F;
94   - scaleFactor = 1.4142135623730950488016887242097F;
95   - stepFactor = 0.5F;
96   - }
  201 + fs << CC_STAGE_TYPE << CC_BOOST;
  202 + fs << CC_FEATURE_TYPE << CC_LBP;
  203 + fs << CC_HEIGHT << 24;
  204 + fs << CC_WIDTH << 24;
97 205  
98   - void _next()
99   - {
100   - src = negs[negIdx++];
  206 + CascadeBoostParams stageParams(CvBoost::GINI, 0.999, 0.5, 0.95, 1, 200);
  207 + fs << CC_STAGE_PARAMS << "{"; stageParams.write( fs ); fs << "}";
  208 +
  209 + fs << CC_FEATURE_PARAMS << "{";
  210 + fs << CC_MAX_CAT_COUNT << stages.first()->maxCatCount();
  211 + fs << CC_FEATURE_SIZE << 1;
  212 + fs << "}";
  213 +
  214 + fs << CC_STAGE_NUM << stages.size();
  215 +
  216 + char cmnt[30];
  217 + int i = 0;
  218 + fs << CC_STAGES << "[";
  219 + foreach (const Classifier *stage, stages) {
  220 + sprintf( cmnt, "stage %d", i );
  221 + cvWriteComment( fs.fs, cmnt, 0 );
  222 + fs << "{";
  223 + stage->write(fs, featureMap);
  224 + fs << "}";
  225 + }
  226 + fs << "]";
  227 + }
101 228  
102   - round += negIdx / negs.size();
103   - round %= (winSize.width * winSize.height);
104   - negIdx %= negs.size();
  229 + void writeFeatures(FileStorage &fs, const Mat& featureMap) const
  230 + {
  231 + stages.first()->writeFeatures(fs, featureMap);
  232 + }
105 233  
106   - point = offset = Point(std::min(round % winSize.width, src.cols - winSize.width),
107   - std::min(round / winSize.width, src.rows - winSize.height));
  234 +private:
  235 + float fillTrainingSet(ImageHandler &imgHandler, QList<Mat> &images, QList<float> &labels)
  236 + {
  237 + imgHandler.restart();
108 238  
109   - scale = max(((float)winSize.width + point.x) / ((float)src.cols),
110   - ((float)winSize.height + point.y) / ((float)src.rows));
  239 + while (images.size() < numPos) {
  240 + Mat pos(imgHandler.winSize, CV_8UC1);
  241 + if (!imgHandler.getPos(pos))
  242 + qFatal("Cannot get another positive sample!");
111 243  
112   - Size sz((int)(scale*src.cols + 0.5F), (int)(scale*src.rows + 0.5F));
113   - resize(src, img, sz);
  244 + if (classify(pos) == 1.0f) {
  245 + images.append(pos);
  246 + labels.append(1.0f);
  247 + }
114 248 }
115 249  
116   - Mat get()
117   - {
118   - if (img.empty())
119   - _next();
120   -
121   - Mat neg(winSize, CV_8UC1);
122   - Mat m(winSize.height, winSize.width, CV_8UC1, (void*)(img.data + point.y * img.step + point.x * img.elemSize()), img.step);
123   - m.copyTo(neg);
124   -
125   - if ((int)(point.x + (1.0F + stepFactor ) * winSize.width) < img.cols)
126   - point.x += (int)(stepFactor * winSize.width);
127   - else {
128   - point.x = offset.x;
129   - if ((int)( point.y + (1.0F + stepFactor ) * winSize.height ) < img.rows)
130   - point.y += (int)(stepFactor * winSize.height);
131   - else {
132   - point.y = offset.y;
133   - scale *= scaleFactor;
134   - if( scale <= 1.0F )
135   - resize(src, img, Size( (int)(scale*src.cols), (int)(scale*src.rows)));
136   - else
137   - _next();
138   - }
  250 + int posCount = images.size();
  251 + qDebug() << "POS count : consumed " << posCount << ":" << imgHandler.posIdx;
  252 +
  253 + int passedNegs = 0;
  254 + while ((images.size() - posCount) < numNegs) {
  255 + Mat neg(imgHandler.winSize, CV_8UC1);
  256 + if (!imgHandler.getNeg(neg))
  257 + qFatal("Cannot get another negative sample!");
  258 +
  259 + if (classify(neg) == 1.0f) {
  260 + images.append(neg);
  261 + labels.append(0.0f);
139 262 }
140   - return neg;
  263 + passedNegs++;
141 264 }
142 265  
143   - QList<Mat> negs;
144   - int negIdx, round;
145   - Mat src, img;
146   - float scale;
147   - float scaleFactor;
148   - float stepFactor;
149   - Size winSize;
150   - Point offset, point;
151   - };
  266 + double acceptanceRatio = (images.size() - posCount) / (double)passedNegs;
  267 + qDebug() << "NEG count : acceptanceRatio " << images.size() - posCount << ":" << acceptanceRatio;
  268 + return acceptanceRatio;
  269 + }
152 270 };
153 271  
154 272 BR_REGISTER(Classifier, _CascadeClassifier)
... ...
openbr/plugins/imgproc/slidingwindow.cpp
... ... @@ -26,63 +26,27 @@ using namespace cv;
26 26  
27 27 namespace br
28 28 {
29   -/*
30   -class CascadeResourceMaker : public ResourceMaker<CascadeClassifier>
31   -{
32   - QString file;
33   -
34   -public:
35   - CascadeResourceMaker(const QString &model)
36   - {
37   - file = Globals->sdkPath + "/share/openbr/models/";
38   - if (model == "Ear") file += "haarcascades/haarcascade_ear.xml";
39   - else if (model == "Eye") file += "haarcascades/haarcascade_eye_tree_eyeglasses.xml";
40   - else if (model == "FrontalFace") file += "haarcascades/haarcascade_frontalface_alt2.xml";
41   - else if (model == "ProfileFace") file += "haarcascades/haarcascade_profileface.xml";
42   - else {
43   - // Create a directory for trainable cascades
44   - file += "openbrcascades/"+model+"/cascade.xml";
45   - QFile touchFile(file);
46   - QtUtils::touchDir(touchFile);
47   - }
48   - }
49 29  
50   -private:
51   - CascadeClassifier *make() const
52   - {
53   - CascadeClassifier *cascade = new CascadeClassifier();
54   - if (!cascade->load(file.toStdString()))
55   - qFatal("Failed to load: %s", qPrintable(file));
56   - return cascade;
57   - }
58   -};
59   -*/
60 30 /*!
61 31 * \ingroup transforms
62 32 * \brief Applies a classifier to a sliding window.
63 33 * Discards negative detections.
64 34 * \author Jordan Cheney \cite JordanCheney
65 35 */
66   - /*
  36 +
67 37 class SlidingWindowTransform : public Transform
68 38 {
69 39 Q_OBJECT
70 40  
71 41 Q_PROPERTY(br::Classifier *classifier READ get_classifier WRITE set_classifier RESET reset_classifier STORED false)
  42 + Q_PROPERTY(QString cascadeDir READ get_cascadeDir WRITE set_cascadeDir RESET reset_cascadeDir STORED false)
72 43 Q_PROPERTY(QString vecFile READ get_vecFile WRITE set_vecFile RESET reset_vecFile STORED false)
73 44 Q_PROPERTY(QString negFile READ get_negFile WRITE set_negFile RESET reset_negFile STORED false)
74   - Q_PROPERTY(int minSize READ get_minSize WRITE set_minSize RESET reset_minSize STORED false)
75   - Q_PROPERTY(int maxSize READ get_maxSize WRITE set_maxSize RESET reset_maxSize STORED false)
76   - Q_PROPERTY(float scaleFactor READ get_scaleFactor WRITE set_scaleFactor RESET reset_scaleFactor STORED false)
77 45  
78 46 BR_PROPERTY(br::Classifier *, classifier, NULL)
  47 + BR_PROPERTY(QString, cascadeDir, "")
79 48 BR_PROPERTY(QString, vecFile, "vec.vec")
80 49 BR_PROPERTY(QString, negFile, "neg.txt")
81   - BR_PROPERTY(int, minSize, 20)
82   - BR_PROPERTY(int, maxSize, -1)
83   - BR_PROPERTY(float, scaleFactor, 1.2)
84   -
85   - Resource<CascadeClassifier> cascadeResource;
86 50  
87 51 QList<Mat> getPos()
88 52 {
... ... @@ -159,9 +123,7 @@ class SlidingWindowTransform : public Transform
159 123  
160 124 void init()
161 125 {
162   - cascadeResource.setResourceMaker(new CascadeResourceMaker(model));
163   - if (model == "Ear" || model == "Eye" || model == "FrontalFace" || model == "ProfileFace")
164   - this->trainable = false;
  126 + cascadeDir = Globals->sdkPath + "/share/openbr/models/openbrcascades/" + cascadeDir;
165 127 }
166 128  
167 129 void train(const TemplateList &_data)
... ... @@ -183,118 +145,38 @@ class SlidingWindowTransform : public Transform
183 145 }
184 146  
185 147 classifier->train(images, labels);
186   - }
187 148  
188   - void project(const Template &src, Template &dst) const
189   - {
190   - TemplateList temp;
191   - project(TemplateList() << src, temp);
192   - if (!temp.isEmpty()) dst = temp.first();
  149 + // save the cascade
  150 + std::string filename = cascadeDir.toStdString() + "/cascade.xml";
  151 + FileStorage fs(filename, FileStorage::WRITE );
  152 +
  153 + if ( !fs.isOpened() )
  154 + return;
  155 +
  156 + fs << FileStorage::getDefaultObjectName(filename) << "{";
  157 +
  158 + Mat featureMap(1, classifier->numFeatures(), CV_32SC1);
  159 + featureMap.setTo(Scalar(-1));
  160 +
  161 + classifier->getUsedFeatures(featureMap);
  162 +
  163 + for (int fi = 0, idx = 0; fi < classifier->numFeatures(); fi++)
  164 + if (featureMap.at<int>(0, fi) >= 0)
  165 + featureMap.ptr<int>(0)[fi] = idx++;
  166 +
  167 + classifier->write(fs, featureMap);
  168 + classifier->writeFeatures(fs, featureMap);
  169 +
  170 + fs << "}";
193 171 }
194 172  
195   - void project(const TemplateList &src, TemplateList &dst) const
  173 + void project(const Template &src, Template &dst) const
196 174 {
197   - CascadeClassifier *cascade = cascadeResource.acquire();
198   - foreach (const Template &t, src) {
199   - const bool enrollAll = t.file.getBool("enrollAll");
200   -
201   - // Mirror the behavior of ExpandTransform in the special case
202   - // of an empty template.
203   - if (t.empty() && !enrollAll) {
204   - dst.append(t);
205   - continue;
206   - }
207   -
208   - for (int i=0; i<t.size(); i++) {
209   - Mat m;
210   - OpenCVUtils::cvtUChar(t[i], m);
211   -
212   - Size maxSize(m.cols, m.rows);
213   -
214   - Mat imageBuffer(m.rows + 1, m.cols + 1, CV_8U);
215   -
216   - QList<Rect> rects;
217   - QList<int> levels;
218   - QList<double> weights;
219   -
220   - for (double factor = 1; ; factor *= 1.2) {
221   - Size originalWindowSize = Size(winWidth, winHeight);
222   -
223   - Size windowSize( cvRound(originalWindowSize.width*factor), cvRound(originalWindowSize.height*factor) );
224   - Size scaledImageSize( cvRound( m.cols/factor ), cvRound( m.rows/factor ) );
225   - Size processingRectSize( scaledImageSize.width - originalWindowSize.width, scaledImageSize.height - originalWindowSize.height );
226   -
227   - if( processingRectSize.width <= 0 || processingRectSize.height <= 0 )
228   - break;
229   - if( windowSize.width > maxSize.width || windowSize.height > maxSize.height )
230   - break;
231   - if( windowSize.width < minSize || windowSize.height < minSize )
232   - continue;
233   -
234   - Mat scaledImage( scaledImageSize, CV_8U, imageBuffer.data );
235   - resize( m, scaledImage, scaledImageSize, 0, 0, CV_INTER_LINEAR );
236   -
237   - int yStep = factor > 2. ? 1 : 2;
238   - int stripCount, stripSize;
239   -
240   - const int PTS_PER_THREAD = 1000;
241   - stripCount = ((processingRectSize.width/yStep)*(processingRectSize.height + yStep-1)/yStep + PTS_PER_THREAD/2)/PTS_PER_THREAD;
242   - stripCount = std::min(std::max(stripCount, 1), 100);
243   - stripSize = (((processingRectSize.height + stripCount - 1)/stripCount + yStep-1)/yStep)*yStep;
244   -
245   - if( !cascade->setImage( scaledImage ) )
246   - qFatal("Can't set an image. Don't know why");
247   -
248   - for( int y = 0; y < min(stripCount * stripSize, processingRectSize.height); y += yStep ) {
249   - for( int x = 0; x < processingRectSize.width; x += yStep ) {
250   - double gypWeight;
251   - int result = cascade->runAt(cascade->featureEvaluator, Point(x, y), gypWeight);
252   -
253   - if (ROCMode) {
254   - if (result == 1)
255   - result = -(int)cascade->data.stages.size();
256   - if (cascade->data.stages.size() + result < 4 ) {
257   - rects.append(Rect(cvRound(x*factor), cvRound(y*factor), windowSize.width, windowSize.height));
258   - levels.append(-result);
259   - weights.append(gypWeight);
260   - }
261   - }
262   - else if( result > 0 ) {
263   - rects.append(Rect(cvRound(x*factor), cvRound(y*factor), windowSize.width, windowSize.height));
264   - }
265   - if( result == 0 )
266   - x += yStep;
267   - }
268   - }
269   - }
270   -
271   - if (ROCMode)
272   - groupRectangles(rects, levels, weights, minNeighbors, 0.2);
273   - else
274   - groupRectangles(rects, minNeighbors, 0.2);
275   -
276   - if (!enrollAll && rects.empty())
277   - rects.append(Rect(0, 0, m.cols, m.rows));
278   -
279   - for (int j = 0; j < rects.size(); j++) {
280   - Template u(t.file, m);
281   - if (levels.size() > j)
282   - u.file.set("Confidence", levels[j]*weights[j]);
283   - else
284   - u.file.set("Confidence", 1);
285   - const QRectF rect = OpenCVUtils::fromRect(rects[j]);
286   - u.file.appendRect(rect);
287   - u.file.set(model, rect);
288   - dst.append(u);
289   - }
290   - }
291   - }
292   - cascadeResource.release(cascade);
  175 + (void)src; (void)dst;
293 176 }
294 177 };
295 178  
296 179 BR_REGISTER(Transform, SlidingWindowTransform)
297   -*/
298 180  
299 181 } // namespace br
300 182  
... ...
openbr/plugins/representation/mblbp.cpp
... ... @@ -22,11 +22,6 @@ class MBLBPRepresentation : public Representation
22 22 {
23 23 Q_OBJECT
24 24  
25   - Q_PROPERTY(int winWidth READ get_winWidth WRITE set_winWidth RESET reset_winWidth STORED false)
26   - Q_PROPERTY(int winHeight READ get_winHeight WRITE set_winHeight RESET reset_winHeight STORED false)
27   - BR_PROPERTY(int, winWidth, 24)
28   - BR_PROPERTY(int, winHeight, 24)
29   -
30 25 void init()
31 26 {
32 27 int offset = winWidth + 1;
... ... @@ -38,11 +33,14 @@ class MBLBPRepresentation : public Representation
38 33 features.append(Feature(offset, x, y, w, h ) );
39 34 }
40 35  
41   - Mat preprocess(const Mat &image) const
  36 + void preprocess(const Mat &src, Mat &dst) const
  37 + {
  38 + integral(src, dst);
  39 + }
  40 +
  41 + float evaluate(const Mat &image, int idx) const
42 42 {
43   - Mat integralImage;
44   - integral(image, integralImage);
45   - return integralImage;
  43 + return (float)features[idx].calc(image);
46 44 }
47 45  
48 46 Mat evaluate(const Mat &image, const QList<int> &indices) const
... ... @@ -55,7 +53,9 @@ class MBLBPRepresentation : public Representation
55 53  
56 54 void write(FileStorage &fs, const Mat &featureMap);
57 55 int numFeatures() const { return features.size(); }
58   - Size windowSize() const { return Size(winWidth + 1, winHeight + 1); }
  56 + Size preWindowSize() const { return Size(winWidth, winHeight); }
  57 + Size postWindowSize() const { return Size(winWidth + 1, winHeight + 1); }
  58 + int maxCatCount() const { return 256; }
59 59  
60 60 struct Feature
61 61 {
... ... @@ -100,17 +100,17 @@ MBLBPRepresentation::Feature::Feature( int offset, int x, int y, int _blockWidth
100 100  
101 101 inline uchar MBLBPRepresentation::Feature::calc(const Mat &img) const
102 102 {
103   - const int* psum = img.ptr<int>();
104   - int cval = psum[p[5]] - psum[p[6]] - psum[p[9]] + psum[p[10]];
105   -
106   - return (uchar)((psum[p[0]] - psum[p[1]] - psum[p[4]] + psum[p[5]] >= cval ? 128 : 0) | // 0
107   - (psum[p[1]] - psum[p[2]] - psum[p[5]] + psum[p[6]] >= cval ? 64 : 0) | // 1
108   - (psum[p[2]] - psum[p[3]] - psum[p[6]] + psum[p[7]] >= cval ? 32 : 0) | // 2
109   - (psum[p[6]] - psum[p[7]] - psum[p[10]] + psum[p[11]] >= cval ? 16 : 0) | // 5
110   - (psum[p[10]] - psum[p[11]] - psum[p[14]] + psum[p[15]] >= cval ? 8 : 0) | // 8
111   - (psum[p[9]] - psum[p[10]] - psum[p[13]] + psum[p[14]] >= cval ? 4 : 0) | // 7
112   - (psum[p[8]] - psum[p[9]] - psum[p[12]] + psum[p[13]] >= cval ? 2 : 0) | // 6
113   - (psum[p[4]] - psum[p[5]] - psum[p[8]] + psum[p[9]] >= cval ? 1 : 0)); // 3
  103 + const int* ptr = img.ptr<int>();
  104 + int cval = ptr[p[5]] - ptr[p[6]] - ptr[p[9]] + ptr[p[10]];
  105 +
  106 + return (uchar)((ptr[p[0]] - ptr[p[1]] - ptr[p[4]] + ptr[p[5]] >= cval ? 128 : 0) | // 0
  107 + (ptr[p[1]] - ptr[p[2]] - ptr[p[5]] + ptr[p[6]] >= cval ? 64 : 0) | // 1
  108 + (ptr[p[2]] - ptr[p[3]] - ptr[p[6]] + ptr[p[7]] >= cval ? 32 : 0) | // 2
  109 + (ptr[p[6]] - ptr[p[7]] - ptr[p[10]] + ptr[p[11]] >= cval ? 16 : 0) | // 5
  110 + (ptr[p[10]] - ptr[p[11]] - ptr[p[14]] + ptr[p[15]] >= cval ? 8 : 0) | // 8
  111 + (ptr[p[9]] - ptr[p[10]] - ptr[p[13]] + ptr[p[14]] >= cval ? 4 : 0) | // 7
  112 + (ptr[p[8]] - ptr[p[9]] - ptr[p[12]] + ptr[p[13]] >= cval ? 2 : 0) | // 6
  113 + (ptr[p[4]] - ptr[p[5]] - ptr[p[8]] + ptr[p[9]] >= cval ? 1 : 0)); // 3
114 114 }
115 115  
116 116 } // namespace br
... ...