Commit 447ef443d15967d13129cf07ea40db9f8db87b5a
1 parent
edcbc8b8
More steps towards representations and proper formatting in backend
Showing
9 changed files
with
341 additions
and
383 deletions
openbr/core/boost.cpp
| @@ -1158,6 +1158,7 @@ float CascadeBoost::predict( int sampleIdx, bool returnSum ) const | @@ -1158,6 +1158,7 @@ float CascadeBoost::predict( int sampleIdx, bool returnSum ) const | ||
| 1158 | { | 1158 | { |
| 1159 | CV_Assert( weak ); | 1159 | CV_Assert( weak ); |
| 1160 | double sum = 0; | 1160 | double sum = 0; |
| 1161 | + | ||
| 1161 | CvSeqReader reader; | 1162 | CvSeqReader reader; |
| 1162 | cvStartReadSeq( weak, &reader ); | 1163 | cvStartReadSeq( weak, &reader ); |
| 1163 | cvSetSeqReaderPos( &reader, 0 ); | 1164 | cvSetSeqReaderPos( &reader, 0 ); |
| @@ -1167,6 +1168,7 @@ float CascadeBoost::predict( int sampleIdx, bool returnSum ) const | @@ -1167,6 +1168,7 @@ float CascadeBoost::predict( int sampleIdx, bool returnSum ) const | ||
| 1167 | CV_READ_SEQ_ELEM( wtree, reader ); | 1168 | CV_READ_SEQ_ELEM( wtree, reader ); |
| 1168 | sum += ((CascadeBoostTree*)wtree)->predict(sampleIdx)->value; | 1169 | sum += ((CascadeBoostTree*)wtree)->predict(sampleIdx)->value; |
| 1169 | } | 1170 | } |
| 1171 | + | ||
| 1170 | if( !returnSum ) | 1172 | if( !returnSum ) |
| 1171 | sum = sum < threshold - CV_THRESHOLD_EPS ? 0.0 : 1.0; | 1173 | sum = sum < threshold - CV_THRESHOLD_EPS ? 0.0 : 1.0; |
| 1172 | return (float)sum; | 1174 | return (float)sum; |
openbr/core/cascade.cpp
| @@ -130,10 +130,12 @@ bool BrCascadeClassifier::train(const string _cascadeDirName, | @@ -130,10 +130,12 @@ bool BrCascadeClassifier::train(const string _cascadeDirName, | ||
| 130 | numStages = _numStages; | 130 | numStages = _numStages; |
| 131 | imgReader.create(_posImages, _negImages, winSize); | 131 | imgReader.create(_posImages, _negImages, winSize); |
| 132 | 132 | ||
| 133 | + Representation *representation = Representation::make("MBLBP(24,24)", NULL); | ||
| 134 | + | ||
| 133 | stageParams = new CascadeBoostParams; | 135 | stageParams = new CascadeBoostParams; |
| 134 | *stageParams = _stageParams; | 136 | *stageParams = _stageParams; |
| 135 | featureEvaluator = new FeatureEvaluator; | 137 | featureEvaluator = new FeatureEvaluator; |
| 136 | - featureEvaluator->init(numPos + numNeg, winSize); | 138 | + featureEvaluator->init(representation, numPos + numNeg); |
| 137 | stageClassifiers.reserve( numStages ); | 139 | stageClassifiers.reserve( numStages ); |
| 138 | 140 | ||
| 139 | double requiredLeafFARate = pow( (double) stageParams->maxFalseAlarm, (double) numStages ) / | 141 | double requiredLeafFARate = pow( (double) stageParams->maxFalseAlarm, (double) numStages ) / |
openbr/core/features.cpp
| @@ -5,65 +5,22 @@ using namespace br; | @@ -5,65 +5,22 @@ using namespace br; | ||
| 5 | 5 | ||
| 6 | //------------------------------------- FeatureEvaluator --------------------------------------- | 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 | cls.create( (int)_maxSampleCount, 1, CV_32FC1 ); | 12 | cls.create( (int)_maxSampleCount, 1, CV_32FC1 ); |
| 15 | - | ||
| 16 | - maxCatCount = 256; | ||
| 17 | - featSize = 1; | ||
| 18 | - | ||
| 19 | - generateFeatures(); | ||
| 20 | } | 13 | } |
| 21 | 14 | ||
| 22 | void FeatureEvaluator::setImage(const Mat &img, uchar clsLabel, int idx) | 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 | cls.ptr<float>(idx)[0] = clsLabel; | 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,90 +48,28 @@ | ||
| 48 | #define TIME( arg ) (time( arg )) | 48 | #define TIME( arg ) (time( arg )) |
| 49 | #endif | 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 | namespace br | 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 | class FeatureEvaluator | 54 | class FeatureEvaluator |
| 80 | { | 55 | { |
| 81 | public: | 56 | public: |
| 82 | ~FeatureEvaluator() {} | 57 | ~FeatureEvaluator() {} |
| 83 | - void init(int _maxSampleCount, cv::Size _winSize); | 58 | + void init(Representation *_representation, int _maxSampleCount); |
| 84 | void setImage(const cv::Mat& img, uchar clsLabel, int idx); | 59 | void setImage(const cv::Mat& img, uchar clsLabel, int idx); |
| 85 | void writeFeatures(cv::FileStorage &fs, const cv::Mat& featureMap) const; | 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 | const cv::Mat& getCls() const { return cls; } | 66 | const cv::Mat& getCls() const { return cls; } |
| 92 | float getCls(int si) const { return cls.at<float>(si, 0); } | 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 | cv::Mat data, cls; | 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 | } // namespace br | 73 | } // namespace br |
| 136 | 74 | ||
| 137 | #endif // FEATURE_H | 75 | #endif // FEATURE_H |
openbr/openbr_plugin.h
| @@ -1395,18 +1395,27 @@ class BR_EXPORT Representation : public Object | @@ -1395,18 +1395,27 @@ class BR_EXPORT Representation : public Object | ||
| 1395 | { | 1395 | { |
| 1396 | Q_OBJECT | 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 | public: | 1403 | public: |
| 1399 | virtual ~Representation() {} | 1404 | virtual ~Representation() {} |
| 1400 | 1405 | ||
| 1401 | static Representation *make(QString str, QObject *parent); /*!< \brief Make a representation from a string. */ | 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 | virtual void train(const QList<cv::Mat> &images, const QList<float> &labels) { (void) images; (void)labels; } | 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 | // By convention, an empty indices list will result in all feature responses being calculated | 1411 | // By convention, an empty indices list will result in all feature responses being calculated |
| 1405 | // and returned. | 1412 | // and returned. |
| 1406 | virtual cv::Mat evaluate(const cv::Mat &image, const QList<int> &indices = QList<int>()) const = 0; | 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 | virtual int numFeatures() const = 0; | 1417 | virtual int numFeatures() const = 0; |
| 1409 | - virtual cv::Size windowSize() const = 0; | 1418 | + virtual int maxCatCount() const = 0; |
| 1410 | 1419 | ||
| 1411 | // Temporary for OpenCV compatibility | 1420 | // Temporary for OpenCV compatibility |
| 1412 | virtual void write( cv::FileStorage &fs, const cv::Mat &featureMap ) { (void)fs; (void)featureMap; } | 1421 | virtual void write( cv::FileStorage &fs, const cv::Mat &featureMap ) { (void)fs; (void)featureMap; } |
| @@ -1421,12 +1430,20 @@ public: | @@ -1421,12 +1430,20 @@ public: | ||
| 1421 | 1430 | ||
| 1422 | static Classifier *make(QString str, QObject *parent); /*!< \brief Make a classifier from a string. */ | 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 | virtual void train(const QList<cv::Mat> &images, const QList<float> &labels) = 0; | 1433 | virtual void train(const QList<cv::Mat> &images, const QList<float> &labels) = 0; |
| 1427 | // By convention, classify should return a value normalized such that the threshold is 0. Negative values | 1434 | // By convention, classify should return a value normalized such that the threshold is 0. Negative values |
| 1428 | // can be interpreted as a negative classification and positive values as a positive classification. | 1435 | // can be interpreted as a negative classification and positive values as a positive classification. |
| 1429 | virtual float classify(const cv::Mat &image) const = 0; | 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,12 +11,14 @@ class BoostedForestClassifier : public Classifier | ||
| 11 | { | 11 | { |
| 12 | Q_OBJECT | 12 | Q_OBJECT |
| 13 | 13 | ||
| 14 | + Q_PROPERTY(br::Representation *representation READ get_representation WRITE set_representation RESET reset_representation STORED false) | ||
| 14 | Q_PROPERTY(float minTAR READ get_minTAR WRITE set_minTAR RESET reset_minTAR STORED false) | 15 | Q_PROPERTY(float minTAR READ get_minTAR WRITE set_minTAR RESET reset_minTAR STORED false) |
| 15 | Q_PROPERTY(float maxFAR READ get_maxFAR WRITE set_maxFAR RESET reset_maxFAR STORED false) | 16 | Q_PROPERTY(float maxFAR READ get_maxFAR WRITE set_maxFAR RESET reset_maxFAR STORED false) |
| 16 | Q_PROPERTY(float trimRate READ get_trimRate WRITE set_trimRate RESET reset_trimRate STORED false) | 17 | Q_PROPERTY(float trimRate READ get_trimRate WRITE set_trimRate RESET reset_trimRate STORED false) |
| 17 | Q_PROPERTY(int maxDepth READ get_maxDepth WRITE set_maxDepth RESET reset_maxDepth STORED false) | 18 | Q_PROPERTY(int maxDepth READ get_maxDepth WRITE set_maxDepth RESET reset_maxDepth STORED false) |
| 18 | Q_PROPERTY(int maxWeakCount READ get_maxWeakCount WRITE set_maxWeakCount RESET reset_maxWeakCount STORED false) | 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 | BR_PROPERTY(float, minTAR, 0.995) | 22 | BR_PROPERTY(float, minTAR, 0.995) |
| 21 | BR_PROPERTY(float, maxFAR, 0.5) | 23 | BR_PROPERTY(float, maxFAR, 0.5) |
| 22 | BR_PROPERTY(float, trimRate, 0.95) | 24 | BR_PROPERTY(float, trimRate, 0.95) |
| @@ -24,16 +26,56 @@ class BoostedForestClassifier : public Classifier | @@ -24,16 +26,56 @@ class BoostedForestClassifier : public Classifier | ||
| 24 | BR_PROPERTY(int, maxWeakCount, 100) | 26 | BR_PROPERTY(int, maxWeakCount, 100) |
| 25 | 27 | ||
| 26 | CascadeBoost *boost; | 28 | CascadeBoost *boost; |
| 29 | + FeatureEvaluator *featureEvaluator; | ||
| 27 | 30 | ||
| 28 | void train(const QList<Mat> &images, const QList<float> &labels) | 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 | float classify(const Mat &image) const | 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 | #include <openbr/plugins/openbr_internal.h> | 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 | using namespace cv; | 5 | using namespace cv; |
| 5 | 6 | ||
| 6 | namespace br | 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 | class _CascadeClassifier : public Classifier | 120 | class _CascadeClassifier : public Classifier |
| 10 | { | 121 | { |
| 11 | Q_OBJECT | 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 | Q_PROPERTY(int numStages READ get_numStages WRITE set_numStages RESET reset_numStages STORED false) | 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 | Q_PROPERTY(int numNegs READ get_numNegs WRITE set_numNegs RESET reset_numNegs STORED false) | 127 | Q_PROPERTY(int numNegs READ get_numNegs WRITE set_numNegs RESET reset_numNegs STORED false) |
| 16 | Q_PROPERTY(float maxFAR READ get_maxFAR WRITE set_maxFAR RESET reset_maxFAR STORED false) | 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 | BR_PROPERTY(int, numStages, 20) | 131 | BR_PROPERTY(int, numStages, 20) |
| 132 | + BR_PROPERTY(int, numPos, 1000) | ||
| 20 | BR_PROPERTY(int, numNegs, 1000) | 133 | BR_PROPERTY(int, numNegs, 1000) |
| 21 | BR_PROPERTY(float, maxFAR, pow(0.5, numStages)) | 134 | BR_PROPERTY(float, maxFAR, pow(0.5, numStages)) |
| 22 | 135 | ||
| @@ -28,127 +141,132 @@ class _CascadeClassifier : public Classifier | @@ -28,127 +141,132 @@ class _CascadeClassifier : public Classifier | ||
| 28 | for (int i = 0; i < images.size(); i++) | 141 | for (int i = 0; i < images.size(); i++) |
| 29 | labels[i] == 1 ? posImages.append(images[i]) : negImages.append(images[i]); | 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 | for (int i = 0; i < numStages; i++) { | 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 | if (currFAR < maxFAR) { | 157 | if (currFAR < maxFAR) { |
| 38 | qDebug() << "FAR is below required level! Terminating early"; | 158 | qDebug() << "FAR is below required level! Terminating early"; |
| 39 | return; | 159 | return; |
| 40 | } | 160 | } |
| 41 | 161 | ||
| 42 | - Classifier *next_stage = stage->clone(); | 162 | + Classifier *next_stage = Classifier::make(stageDescription, NULL); |
| 43 | next_stage->train(trainingImages, trainingLabels); | 163 | next_stage->train(trainingImages, trainingLabels); |
| 44 | stages.append(next_stage); | 164 | stages.append(next_stage); |
| 165 | + | ||
| 166 | + qDebug() << "END>"; | ||
| 45 | } | 167 | } |
| 46 | } | 168 | } |
| 47 | 169 | ||
| 48 | float classify(const Mat &image) const | 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 | BR_REGISTER(Classifier, _CascadeClassifier) | 272 | BR_REGISTER(Classifier, _CascadeClassifier) |
openbr/plugins/imgproc/slidingwindow.cpp
| @@ -26,63 +26,27 @@ using namespace cv; | @@ -26,63 +26,27 @@ using namespace cv; | ||
| 26 | 26 | ||
| 27 | namespace br | 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 | * \ingroup transforms | 31 | * \ingroup transforms |
| 62 | * \brief Applies a classifier to a sliding window. | 32 | * \brief Applies a classifier to a sliding window. |
| 63 | * Discards negative detections. | 33 | * Discards negative detections. |
| 64 | * \author Jordan Cheney \cite JordanCheney | 34 | * \author Jordan Cheney \cite JordanCheney |
| 65 | */ | 35 | */ |
| 66 | - /* | 36 | + |
| 67 | class SlidingWindowTransform : public Transform | 37 | class SlidingWindowTransform : public Transform |
| 68 | { | 38 | { |
| 69 | Q_OBJECT | 39 | Q_OBJECT |
| 70 | 40 | ||
| 71 | Q_PROPERTY(br::Classifier *classifier READ get_classifier WRITE set_classifier RESET reset_classifier STORED false) | 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 | Q_PROPERTY(QString vecFile READ get_vecFile WRITE set_vecFile RESET reset_vecFile STORED false) | 43 | Q_PROPERTY(QString vecFile READ get_vecFile WRITE set_vecFile RESET reset_vecFile STORED false) |
| 73 | Q_PROPERTY(QString negFile READ get_negFile WRITE set_negFile RESET reset_negFile STORED false) | 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 | BR_PROPERTY(br::Classifier *, classifier, NULL) | 46 | BR_PROPERTY(br::Classifier *, classifier, NULL) |
| 47 | + BR_PROPERTY(QString, cascadeDir, "") | ||
| 79 | BR_PROPERTY(QString, vecFile, "vec.vec") | 48 | BR_PROPERTY(QString, vecFile, "vec.vec") |
| 80 | BR_PROPERTY(QString, negFile, "neg.txt") | 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 | QList<Mat> getPos() | 51 | QList<Mat> getPos() |
| 88 | { | 52 | { |
| @@ -159,9 +123,7 @@ class SlidingWindowTransform : public Transform | @@ -159,9 +123,7 @@ class SlidingWindowTransform : public Transform | ||
| 159 | 123 | ||
| 160 | void init() | 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 | void train(const TemplateList &_data) | 129 | void train(const TemplateList &_data) |
| @@ -183,118 +145,38 @@ class SlidingWindowTransform : public Transform | @@ -183,118 +145,38 @@ class SlidingWindowTransform : public Transform | ||
| 183 | } | 145 | } |
| 184 | 146 | ||
| 185 | classifier->train(images, labels); | 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 | BR_REGISTER(Transform, SlidingWindowTransform) | 179 | BR_REGISTER(Transform, SlidingWindowTransform) |
| 297 | -*/ | ||
| 298 | 180 | ||
| 299 | } // namespace br | 181 | } // namespace br |
| 300 | 182 |
openbr/plugins/representation/mblbp.cpp
| @@ -22,11 +22,6 @@ class MBLBPRepresentation : public Representation | @@ -22,11 +22,6 @@ class MBLBPRepresentation : public Representation | ||
| 22 | { | 22 | { |
| 23 | Q_OBJECT | 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 | void init() | 25 | void init() |
| 31 | { | 26 | { |
| 32 | int offset = winWidth + 1; | 27 | int offset = winWidth + 1; |
| @@ -38,11 +33,14 @@ class MBLBPRepresentation : public Representation | @@ -38,11 +33,14 @@ class MBLBPRepresentation : public Representation | ||
| 38 | features.append(Feature(offset, x, y, w, h ) ); | 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 | Mat evaluate(const Mat &image, const QList<int> &indices) const | 46 | Mat evaluate(const Mat &image, const QList<int> &indices) const |
| @@ -55,7 +53,9 @@ class MBLBPRepresentation : public Representation | @@ -55,7 +53,9 @@ class MBLBPRepresentation : public Representation | ||
| 55 | 53 | ||
| 56 | void write(FileStorage &fs, const Mat &featureMap); | 54 | void write(FileStorage &fs, const Mat &featureMap); |
| 57 | int numFeatures() const { return features.size(); } | 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 | struct Feature | 60 | struct Feature |
| 61 | { | 61 | { |
| @@ -100,17 +100,17 @@ MBLBPRepresentation::Feature::Feature( int offset, int x, int y, int _blockWidth | @@ -100,17 +100,17 @@ MBLBPRepresentation::Feature::Feature( int offset, int x, int y, int _blockWidth | ||
| 100 | 100 | ||
| 101 | inline uchar MBLBPRepresentation::Feature::calc(const Mat &img) const | 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 | } // namespace br | 116 | } // namespace br |