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 | 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 | ... | ... |