From edcbc8b8fbb3e66c4e46c654d101e49e212a700d Mon Sep 17 00:00:00 2001 From: Jordan Cheney Date: Thu, 30 Apr 2015 01:04:10 -0400 Subject: [PATCH] Clean-up and steps towards incorparting representations in the backend --- openbr/core/boost.cpp | 103 +++++++++++++++++++++++++++++++++++++++---------------------------------------------------------------- openbr/core/boost.h | 74 ++++++++++++++++++++++++++++++-------------------------------------------- openbr/core/cascade.cpp | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------------------------- openbr/core/cascade.h | 46 +++++++++++++++++++++++----------------------- openbr/core/features.cpp | 47 +++++++++++++++++++++++++---------------------- openbr/core/features.h | 105 +++++++++++++++++++++++++++------------------------------------------------------------------------------ openbr/plugins/metadata/cascade.cpp | 17 +++++++---------- openbr/plugins/representation/mblbp.cpp | 13 +++++++------ 8 files changed, 220 insertions(+), 294 deletions(-) diff --git a/openbr/core/boost.cpp b/openbr/core/boost.cpp index c535ea2..b8dcc3b 100644 --- a/openbr/core/boost.cpp +++ b/openbr/core/boost.cpp @@ -171,29 +171,6 @@ void CascadeBoostParams::write( FileStorage &fs ) const fs << CC_WEAK_COUNT << weak_count; } -// ----------------------------- Compatibility ---------------------------------- - -BoostBRCompatibility::BoostBRCompatibility(Representation *_representation, int _numSamples) -{ - representation = _representation; - data.create(_numSamples, representation->windowSize().area(), CV_32SC1); - cls.create(_numSamples, 1, CV_32FC1); -} - -void BoostBRCompatibility::setImage(const Mat &img, uchar clsLabel, int idx) -{ - cls.ptr(idx)[0] = clsLabel; - Mat preprocessed = representation->preprocess(img); - Mat copier(representation->windowSize(), CV_32FC1, data.ptr(idx)); - preprocessed.copyTo(copier); -} - -float BoostBRCompatibility::operator ()(int featureIdx, int sampleIdx) const -{ - return representation->evaluate(data.row(sampleIdx).reshape(0, representation->windowSize().height), - QList() << featureIdx).ptr()[0]; -} - //---------------------------- CascadeBoostTrainData ----------------------------- CvDTreeNode* CascadeBoostTrainData::subsample_data( const CvMat* _subsample_idx ) @@ -361,18 +338,18 @@ CvDTreeNode* CascadeBoostTrainData::subsample_data( const CvMat* _subsample_idx return root; } -CascadeBoostTrainData::CascadeBoostTrainData( const BoostBRCompatibility* _compat, +CascadeBoostTrainData::CascadeBoostTrainData( const FeatureEvaluator* _featureEvaluator, const CvDTreeParams& _params ) { is_classifier = true; - var_all = var_count = _compat->numFeatures(); + var_all = var_count = (int)_featureEvaluator->getNumFeatures(); - compat = _compat; + featureEvaluator = _featureEvaluator; shared = true; set_params( _params ); - max_c_count = MAX( 2, compat->maxCatCount() ); + max_c_count = MAX( 2, featureEvaluator->getMaxCatCount() ); var_type = cvCreateMat( 1, var_count + 2, CV_32SC1 ); - if ( compat->maxCatCount() > 0 ) + if ( featureEvaluator->getMaxCatCount() > 0 ) { numPrecalcIdx = 0; cat_var_count = var_count; @@ -402,15 +379,15 @@ CascadeBoostTrainData::CascadeBoostTrainData( const BoostBRCompatibility* _compa split_heap = cvCreateSet( 0, sizeof(split_heap[0]), maxSplitSize, tree_storage ); } -CascadeBoostTrainData::CascadeBoostTrainData( const BoostBRCompatibility* _compat, +CascadeBoostTrainData::CascadeBoostTrainData( const FeatureEvaluator* _featureEvaluator, int _numSamples, int _precalcValBufSize, int _precalcIdxBufSize, const CvDTreeParams& _params ) { - setData( _compat, _numSamples, _precalcValBufSize, _precalcIdxBufSize, _params ); + setData( _featureEvaluator, _numSamples, _precalcValBufSize, _precalcIdxBufSize, _params ); } -void CascadeBoostTrainData::setData( const BoostBRCompatibility* _compat, +void CascadeBoostTrainData::setData( const FeatureEvaluator* _featureEvaluator, int _numSamples, int _precalcValBufSize, int _precalcIdxBufSize, const CvDTreeParams& _params ) @@ -421,7 +398,6 @@ void CascadeBoostTrainData::setData( const BoostBRCompatibility* _compat, uint64 effective_buf_size = 0; int effective_buf_height = 0, effective_buf_width = 0; - clear(); shared = true; have_labels = true; @@ -432,17 +408,18 @@ void CascadeBoostTrainData::setData( const BoostBRCompatibility* _compat, set_params( _params ); - compat = _compat; + CV_Assert( _featureEvaluator ); + featureEvaluator = _featureEvaluator; - max_c_count = MAX( 2, compat->maxCatCount() ); - _resp = compat->getCls(); + max_c_count = MAX( 2, featureEvaluator->getMaxCatCount() ); + _resp = featureEvaluator->getCls(); responses = &_resp; // TODO: check responses: elements must be 0 or 1 if( _precalcValBufSize < 0 || _precalcIdxBufSize < 0) CV_Error( CV_StsOutOfRange, "_numPrecalcVal and _numPrecalcIdx must be positive or 0" ); - var_count = var_all = compat->numFeatures(); + var_count = var_all = featureEvaluator->getNumFeatures() * featureEvaluator->getFeatureSize(); sample_count = _numSamples; is_buf_16u = false; @@ -457,7 +434,7 @@ void CascadeBoostTrainData::setData( const BoostBRCompatibility* _compat, valCache.create( numPrecalcVal, sample_count, CV_32FC1 ); var_type = cvCreateMat( 1, var_count + 2, CV_32SC1 ); - if ( compat->maxCatCount() > 0 ) + if ( featureEvaluator->getMaxCatCount() > 0 ) { numPrecalcIdx = 0; cat_var_count = var_count; @@ -623,7 +600,7 @@ void CascadeBoostTrainData::get_ord_var_data( CvDTreeNode* n, int vi, float* ord { int idx = (*sortedIndices)[i]; idx = sampleIndices[idx]; - ordValuesBuf[i] = (*compat)( vi, idx); + ordValuesBuf[i] = (*featureEvaluator)( vi, idx); } } } @@ -645,7 +622,7 @@ void CascadeBoostTrainData::get_ord_var_data( CvDTreeNode* n, int vi, float* ord for( int i = 0; i < nodeSampleCount; i++ ) { sortedIndicesBuf[i] = i; - sampleValues[i] = (*compat)( vi, sampleIndices[i]); + sampleValues[i] = (*featureEvaluator)( vi, sampleIndices[i]); } } icvSortIntAux( sortedIndicesBuf, nodeSampleCount, &sampleValues[0] ); @@ -673,7 +650,7 @@ const int* CascadeBoostTrainData::get_cat_var_data( CvDTreeNode* n, int vi, int* if( vi >= numPrecalcVal && vi < var_count ) { for( int i = 0; i < nodeSampleCount; i++ ) - catValuesBuf[i] = (int)(*compat)( vi, sampleIndices[i] ); + catValuesBuf[i] = (int)(*featureEvaluator)( vi, sampleIndices[i] ); } else { @@ -688,15 +665,14 @@ float CascadeBoostTrainData::getVarValue( int vi, int si ) { if ( vi < numPrecalcVal && !valCache.empty() ) return valCache.at( vi, si ); - return (*compat)( vi, si ); + return (*featureEvaluator)( vi, si ); } - struct FeatureIdxOnlyPrecalc : ParallelLoopBody { - FeatureIdxOnlyPrecalc( const BoostBRCompatibility* _compat, CvMat* _buf, int _sample_count, bool _is_buf_16u ) + FeatureIdxOnlyPrecalc( const FeatureEvaluator* _featureEvaluator, CvMat* _buf, int _sample_count, bool _is_buf_16u ) { - compat = _compat; + featureEvaluator = _featureEvaluator; sample_count = _sample_count; udst = (unsigned short*)_buf->data.s; idst = _buf->data.i; @@ -710,7 +686,7 @@ struct FeatureIdxOnlyPrecalc : ParallelLoopBody { for( int si = 0; si < sample_count; si++ ) { - valCachePtr[si] = (*compat)( fi, si ); + valCachePtr[si] = (*featureEvaluator)( fi, si ); if ( is_buf_16u ) *(udst + fi*sample_count + si) = (unsigned short)si; else @@ -722,7 +698,7 @@ struct FeatureIdxOnlyPrecalc : ParallelLoopBody icvSortIntAux( idst + fi*sample_count, sample_count, valCachePtr ); } } - const BoostBRCompatibility* compat; + const FeatureEvaluator* featureEvaluator; int sample_count; int* idst; unsigned short* udst; @@ -731,9 +707,9 @@ struct FeatureIdxOnlyPrecalc : ParallelLoopBody struct FeatureValAndIdxPrecalc : ParallelLoopBody { - FeatureValAndIdxPrecalc( const BoostBRCompatibility* _compat, CvMat* _buf, Mat* _valCache, int _sample_count, bool _is_buf_16u ) + FeatureValAndIdxPrecalc( const FeatureEvaluator* _featureEvaluator, CvMat* _buf, Mat* _valCache, int _sample_count, bool _is_buf_16u ) { - compat = _compat; + featureEvaluator = _featureEvaluator; valCache = _valCache; sample_count = _sample_count; udst = (unsigned short*)_buf->data.s; @@ -746,7 +722,7 @@ struct FeatureValAndIdxPrecalc : ParallelLoopBody { for( int si = 0; si < sample_count; si++ ) { - valCache->at(fi,si) = (*compat)( fi, si ); + valCache->at(fi,si) = (*featureEvaluator)( fi, si ); if ( is_buf_16u ) *(udst + fi*sample_count + si) = (unsigned short)si; else @@ -758,7 +734,7 @@ struct FeatureValAndIdxPrecalc : ParallelLoopBody icvSortIntAux( idst + fi*sample_count, sample_count, valCache->ptr(fi) ); } } - const BoostBRCompatibility* compat; + const FeatureEvaluator* featureEvaluator; Mat* valCache; int sample_count; int* idst; @@ -768,9 +744,9 @@ struct FeatureValAndIdxPrecalc : ParallelLoopBody struct FeatureValOnlyPrecalc : ParallelLoopBody { - FeatureValOnlyPrecalc( const BoostBRCompatibility* _compat, Mat* _valCache, int _sample_count ) + FeatureValOnlyPrecalc( const FeatureEvaluator* _featureEvaluator, Mat* _valCache, int _sample_count ) { - compat = _compat; + featureEvaluator = _featureEvaluator; valCache = _valCache; sample_count = _sample_count; } @@ -778,9 +754,9 @@ struct FeatureValOnlyPrecalc : ParallelLoopBody { for ( int fi = range.start; fi < range.end; fi++) for( int si = 0; si < sample_count; si++ ) - valCache->at(fi,si) = (*compat)( fi, si ); + valCache->at(fi,si) = (*featureEvaluator)( fi, si ); } - const BoostBRCompatibility* compat; + const FeatureEvaluator* featureEvaluator; Mat* valCache; int sample_count; }; @@ -791,11 +767,11 @@ void CascadeBoostTrainData::precalculate() double proctime = -TIME( 0 ); parallel_for_( Range(numPrecalcVal, numPrecalcIdx), - FeatureIdxOnlyPrecalc(compat, buf, sample_count, is_buf_16u!=0) ); + FeatureIdxOnlyPrecalc(featureEvaluator, buf, sample_count, is_buf_16u!=0) ); parallel_for_( Range(0, minNum), - FeatureValAndIdxPrecalc(compat, buf, &valCache, sample_count, is_buf_16u!=0) ); + FeatureValAndIdxPrecalc(featureEvaluator, buf, &valCache, sample_count, is_buf_16u!=0) ); parallel_for_( Range(minNum, numPrecalcVal), - FeatureValOnlyPrecalc(compat, &valCache, sample_count) ); + FeatureValOnlyPrecalc(featureEvaluator, &valCache, sample_count) ); cout << "Precalculation time: " << (proctime + TIME( 0 )) << endl; } @@ -807,7 +783,7 @@ CvDTreeNode* CascadeBoostTree::predict( int sampleIdx ) const if( !node ) CV_Error( CV_StsError, "The tree has not been trained yet" ); - if ( ((CascadeBoostTrainData*)data)->compat->maxCatCount() == 0 ) // ordered + if ( ((CascadeBoostTrainData*)data)->featureEvaluator->getMaxCatCount() == 0 ) // ordered { while( node->left ) { @@ -830,7 +806,7 @@ CvDTreeNode* CascadeBoostTree::predict( int sampleIdx ) const void CascadeBoostTree::write( FileStorage &fs, const Mat& featureMap ) { - int maxCatCount = ((CascadeBoostTrainData*)data)->compat->maxCatCount(); + int maxCatCount = ((CascadeBoostTrainData*)data)->featureEvaluator->getMaxCatCount(); int subsetN = (maxCatCount + 31)/32; queue internalNodesQueue; int size = (int)pow( 2.f, (float)ensemble->get_params().max_depth); @@ -1123,7 +1099,7 @@ void CascadeBoostTree::markFeaturesInMap( Mat& featureMap ) //----------------------------------- CascadeBoost -------------------------------------- -bool CascadeBoost::train( const BoostBRCompatibility* _compat, +bool CascadeBoost::train( const FeatureEvaluator* _featureEvaluator, int _numSamples, int _precalcValBufSize, int _precalcIdxBufSize, const CascadeBoostParams& _params ) @@ -1132,7 +1108,7 @@ bool CascadeBoost::train( const BoostBRCompatibility* _compat, CV_Assert( !data ); clear(); - data = new CascadeBoostTrainData( _compat, _numSamples, + data = new CascadeBoostTrainData( _featureEvaluator, _numSamples, _precalcValBufSize, _precalcIdxBufSize, _params ); CvMemStorage *storage = cvCreateMemStorage(); @@ -1154,7 +1130,6 @@ bool CascadeBoost::train( const BoostBRCompatibility* _compat, CascadeBoostTree* tree = new CascadeBoostTree; if( !tree->train( data, subsample_mask, this ) ) { - qDebug("Couldn't train tree"); delete tree; break; } @@ -1454,7 +1429,7 @@ bool CascadeBoost::isErrDesired() vector eval(sCount); for( int i = 0; i < sCount; i++ ) - if( ((CascadeBoostTrainData*)data)->compat->getCls( i ) == 1.0F ) + if( ((CascadeBoostTrainData*)data)->featureEvaluator->getCls( i ) == 1.0F ) eval[numPos++] = predict( i, true ); icvSortFlt( &eval[0], numPos, 0 ); int thresholdIdx = (int)((1.0F - minHitRate) * numPos); @@ -1467,7 +1442,7 @@ bool CascadeBoost::isErrDesired() for( int i = 0; i < sCount; i++ ) { - if( ((CascadeBoostTrainData*)data)->compat->getCls( i ) == 0.0F ) + if( ((CascadeBoostTrainData*)data)->featureEvaluator->getCls( i ) == 0.0F ) { numNeg++; if( predict( i ) ) diff --git a/openbr/core/boost.h b/openbr/core/boost.h index 2bb5d1f..c7030c9 100644 --- a/openbr/core/boost.h +++ b/openbr/core/boost.h @@ -13,52 +13,36 @@ struct CascadeBoostParams : CvBoostParams float maxFalseAlarm; CascadeBoostParams(); - CascadeBoostParams( int _boostType, float _minHitRate, float _maxFalseAlarm, - double _weightTrimRate, int _maxDepth, int _maxWeakCount ); + CascadeBoostParams(int _boostType, float _minHitRate, float _maxFalseAlarm, + double _weightTrimRate, int _maxDepth, int _maxWeakCount); virtual ~CascadeBoostParams() {} void write( cv::FileStorage &fs ) const; }; -struct BoostBRCompatibility -{ - BoostBRCompatibility(Representation *_representation, int _numSamples); - void setImage(const cv::Mat &img, uchar clsLabel, int idx); - float operator()(int featureIdx, int sampleIdx) const; - const cv::Mat &getCls() const { return cls; } - float getCls(int idx) const { return cls.at(idx, 0); } - - int numFeatures() const { return representation->numFeatures(); } - int maxCatCount() const { return 256; } - - Representation *representation; - cv::Mat data, cls; -}; - struct CascadeBoostTrainData : CvDTreeTrainData { - CascadeBoostTrainData( const BoostBRCompatibility* _compat, - const CvDTreeParams& _params ); - CascadeBoostTrainData( const BoostBRCompatibility* _compat, - int _numSamples, int _precalcValBufSize, int _precalcIdxBufSize, - const CvDTreeParams& _params = CvDTreeParams() ); - virtual void setData( const BoostBRCompatibility* _compat, + CascadeBoostTrainData(const FeatureEvaluator* _featureEvaluator, const CvDTreeParams& _params); + CascadeBoostTrainData(const FeatureEvaluator* _featureEvaluator, int _numSamples, int _precalcValBufSize, int _precalcIdxBufSize, - const CvDTreeParams& _params=CvDTreeParams() ); + const CvDTreeParams& _params = CvDTreeParams()); + virtual void setData(const FeatureEvaluator* _featureEvaluator, + int _numSamples, int _precalcValBufSize, int _precalcIdxBufSize, + const CvDTreeParams& _params=CvDTreeParams()); void precalculate(); - virtual CvDTreeNode* subsample_data( const CvMat* _subsample_idx ); + virtual CvDTreeNode* subsample_data(const CvMat* _subsample_idx); - virtual const int* get_class_labels( CvDTreeNode* n, int* labelsBuf ); - virtual const int* get_cv_labels( CvDTreeNode* n, int* labelsBuf); - virtual const int* get_sample_indices( CvDTreeNode* n, int* indicesBuf ); + virtual const int* get_class_labels(CvDTreeNode* n, int* labelsBuf); + virtual const int* get_cv_labels(CvDTreeNode* n, int* labelsBuf); + virtual const int* get_sample_indices(CvDTreeNode* n, int* indicesBuf); - virtual void get_ord_var_data( CvDTreeNode* n, int vi, float* ordValuesBuf, int* sortedIndicesBuf, - const float** ordValues, const int** sortedIndices, int* sampleIndicesBuf ); - virtual const int* get_cat_var_data( CvDTreeNode* n, int vi, int* catValuesBuf ); - virtual float getVarValue( int vi, int si ); + virtual void get_ord_var_data(CvDTreeNode* n, int vi, float* ordValuesBuf, int* sortedIndicesBuf, + const float** ordValues, const int** sortedIndices, int* sampleIndicesBuf); + virtual const int* get_cat_var_data(CvDTreeNode* n, int vi, int* catValuesBuf); + virtual float getVarValue(int vi, int si); virtual void free_train_data(); - const BoostBRCompatibility* compat; + const FeatureEvaluator* featureEvaluator; cv::Mat valCache; // precalculated feature values (CV_32FC1) CvMat _resp; // for casting int numPrecalcVal, numPrecalcIdx; @@ -67,27 +51,29 @@ struct CascadeBoostTrainData : CvDTreeTrainData class CascadeBoostTree : public CvBoostTree { public: - virtual CvDTreeNode* predict( int sampleIdx ) const; - void write( cv::FileStorage &fs, const cv::Mat& featureMap ); - void markFeaturesInMap( cv::Mat& featureMap ); + virtual CvDTreeNode* predict(int sampleIdx) const; + void write(cv::FileStorage &fs, const cv::Mat& featureMap); + void markFeaturesInMap(cv::Mat& featureMap); + protected: - virtual void split_node_data( CvDTreeNode* n ); + virtual void split_node_data(CvDTreeNode* n); }; class CascadeBoost : public CvBoost { public: - virtual bool train( const BoostBRCompatibility* _compat, - int _numSamples, int _precalcValBufSize, int _precalcIdxBufSize, - const CascadeBoostParams& _params=CascadeBoostParams() ); + virtual bool train(const FeatureEvaluator *_featureEvaluator, + int _numSamples, int _precalcValBufSize, int _precalcIdxBufSize, + const CascadeBoostParams &_params=CascadeBoostParams()); virtual float predict( int sampleIdx, bool returnSum = false ) const; float getThreshold() const { return threshold; } - void write( cv::FileStorage &fs, const cv::Mat& featureMap ) const; - void markUsedFeaturesInMap( cv::Mat& featureMap ); + void write(cv::FileStorage &fs, const cv::Mat& featureMap) const; + void markUsedFeaturesInMap(cv::Mat& featureMap); + protected: - virtual bool set_params( const CvBoostParams& _params ); - virtual void update_weights( CvBoostTree* tree ); + virtual bool set_params(const CvBoostParams& _params); + virtual void update_weights(CvBoostTree* tree); virtual bool isErrDesired(); float threshold; diff --git a/openbr/core/cascade.cpp b/openbr/core/cascade.cpp index c766102..fa6d477 100644 --- a/openbr/core/cascade.cpp +++ b/openbr/core/cascade.cpp @@ -3,17 +3,17 @@ #include #include +using namespace std; using namespace br; using namespace cv; -bool CascadeImageReader::create( const QList &_posImages, const QList &_negImages, Size _winSize ) +bool CascadeImageReader::create(const QList &_posImages, const QList &_negImages, Size _winSize) { posImages = _posImages; negImages = _negImages; winSize = _winSize; - posIdx = 0; negIdx = 0; - round = 0; + posIdx = negIdx = 0; src.create( 0, 0 , CV_8UC1 ); img.create( 0, 0, CV_8UC1 ); @@ -21,22 +21,32 @@ bool CascadeImageReader::create( const QList &_posImages, const QList scale = 1.0F; scaleFactor = 1.4142135623730950488016887242097F; stepFactor = 0.5F; + round = 0; return true; } bool CascadeImageReader::nextNeg() { - src = negImages[negIdx++]; - - round += negIdx / negImages.size(); - round = round % (winSize.width * winSize.height); - negIdx %= negImages.size(); - - offset.x = std::min( (int)round % winSize.width, src.cols - winSize.width ); - offset.y = std::min( (int)round / winSize.width, src.rows - winSize.height ); + Point _offset = Point(0,0); + size_t count = negImages.size(); + for (size_t i = 0; i < count; i++) { + src = negImages[negIdx++]; + if( src.empty() ) + continue; + round += negIdx / count; + round = round % (winSize.width * winSize.height); + negIdx %= count; + + _offset.x = std::min( (int)round % winSize.width, src.cols - winSize.width ); + _offset.y = std::min( (int)round / winSize.width, src.rows - winSize.height ); + if( !src.empty() && src.type() == CV_8UC1 && _offset.x >= 0 && _offset.y >= 0 ) + break; + } - point = offset; + if( src.empty() ) + return false; // no appropriate image + point = offset = _offset; scale = max( ((float)winSize.width + point.x) / ((float)src.cols), ((float)winSize.height + point.y) / ((float)src.rows) ); @@ -56,7 +66,8 @@ bool CascadeImageReader::getNeg( Mat& _img ) if ( !nextNeg() ) return false; - Mat mat( winSize.height, winSize.width, CV_8UC1, (void*)(img.data + point.y * img.step + point.x * img.elemSize()), img.step ); + Mat mat( winSize.height, winSize.width, CV_8UC1, + (void*)(img.data + point.y * img.step + point.x * img.elemSize()), img.step ); mat.copyTo(_img); if( (int)( point.x + (1.0F + stepFactor ) * winSize.width ) < img.cols ) @@ -82,11 +93,10 @@ bool CascadeImageReader::getNeg( Mat& _img ) return true; } + bool CascadeImageReader::getPos(Mat &_img) { - if (posIdx > (int)posImages.size()) - CV_Error( CV_StsBadArg, "Can not get new positive sample. Not enough positive samples.\n"); - _img = posImages[posIdx++]; + posImages[posIdx++].copyTo(_img); return true; } @@ -95,15 +105,16 @@ bool CascadeImageReader::getPos(Mat &_img) bool BrCascadeClassifier::train(const string _cascadeDirName, const QList &_posImages, const QList &_negImages, + int _numPos, int _numNeg, int _precalcValBufSize, int _precalcIdxBufSize, - int _numPos, int _numNeg, int _numStages, - Representation *_representation, + int _numStages, + Size _winSize, const CascadeBoostParams& _stageParams) { // Start recording clock ticks for training time output const clock_t begin_time = clock(); - if( _cascadeDirName.empty() ) + if (_cascadeDirName.empty()) CV_Error( CV_StsBadArg, "_cascadeDirName is NULL" ); string dirName; @@ -112,14 +123,17 @@ bool BrCascadeClassifier::train(const string _cascadeDirName, else dirName = _cascadeDirName + '/'; - compat = new BoostBRCompatibility(_representation, _numPos + _numNeg); + winSize = _winSize; + numPos = _numPos; numNeg = _numNeg; numStages = _numStages; - imgReader.create(_posImages, _negImages, _representation->windowSize()); + imgReader.create(_posImages, _negImages, winSize); stageParams = new CascadeBoostParams; *stageParams = _stageParams; + featureEvaluator = new FeatureEvaluator; + featureEvaluator->init(numPos + numNeg, winSize); stageClassifiers.reserve( numStages ); double requiredLeafFARate = pow( (double) stageParams->maxFalseAlarm, (double) numStages ) / @@ -127,31 +141,29 @@ bool BrCascadeClassifier::train(const string _cascadeDirName, double tempLeafFARate; for (int i = 0; i < numStages; i++) { - qDebug() << endl << "===== TRAINING " << i << "-stage ====="; - qDebug() << "train( (BoostBRCompatibility*)compat, + bool isStageTrained = tempStage->train( (FeatureEvaluator*)featureEvaluator, curNumSamples, _precalcValBufSize, _precalcIdxBufSize, *((CascadeBoostParams*)stageParams) ); - qDebug() << "END>"; + cout << "END>" << endl; - if(!isStageTrained) + if (!isStageTrained) break; - stageClassifiers.push_back( tempStage ); + stageClassifiers.push_back(tempStage); // Output training time up till now float seconds = float( clock () - begin_time ) / CLOCKS_PER_SEC; @@ -159,12 +171,11 @@ bool BrCascadeClassifier::train(const string _cascadeDirName, int hours = (int(seconds) / 60 / 60) % 24; int minutes = (int(seconds) / 60) % 60; int seconds_left = int(seconds) % 60; - qDebug() << "Training until now has taken " << days << " days " << hours << " hours " << minutes << " minutes " << seconds_left <<" seconds."; + cout << "Training until now has taken " << days << " days " << hours << " hours " << minutes << " minutes " << seconds_left <<" seconds." << endl; } - if(stageClassifiers.size() == 0) - { - qDebug() << "Cascade classifier can't be trained. Check the used training parameters."; + if (stageClassifiers.size() == 0) { + cout << "Cascade classifier can't be trained. Check the used training parameters." << endl; return false; } @@ -193,7 +204,7 @@ bool BrCascadeClassifier::updateTrainingSet( double& acceptanceRatio) int posCount = fillPassedSamples( 0, numPos, true, posConsumed ); if( !posCount ) return false; - qDebug() << "POS count : consumed " << posCount << " : " << (int)posConsumed; + cout << "POS count : consumed " << posCount << " : " << (int)posConsumed << endl; int proNumNeg = cvRound( ( ((double)numNeg) * ((double)posCount) ) / numPos ); // apply only a fraction of negative samples. double is required since overflow is possible int negCount = fillPassedSamples( posCount, proNumNeg, false, negConsumed ); @@ -202,14 +213,14 @@ bool BrCascadeClassifier::updateTrainingSet( double& acceptanceRatio) curNumSamples = posCount + negCount; acceptanceRatio = negConsumed == 0 ? 0 : ( (double)negCount/(double)(int64)negConsumed ); - qDebug() << "NEG count : acceptanceRatio " << negCount << " : " << acceptanceRatio; + cout << "NEG count : acceptanceRatio " << negCount << " : " << acceptanceRatio << endl; return true; } int BrCascadeClassifier::fillPassedSamples( int first, int count, bool isPositive, int64& consumed ) { int getcount = 0; - Mat img(compat->representation->windowSize(), CV_8UC1); + Mat img(winSize, CV_8UC1); for( int i = first; i < first + count; i++ ) { for( ; ; ) @@ -220,7 +231,7 @@ int BrCascadeClassifier::fillPassedSamples( int first, int count, bool isPositiv return getcount; consumed++; - compat->setImage( img, isPositive ? 1 : 0, i ); + featureEvaluator->setImage( img, isPositive ? 1 : 0, i ); if( predict( i ) == 1.0F ) { getcount++; @@ -238,13 +249,18 @@ void BrCascadeClassifier::writeParams( FileStorage &fs ) const fs << CC_FEATURE_TYPE << CC_LBP; fs << CC_HEIGHT << winSize.height; fs << CC_WIDTH << winSize.width; + fs << CC_STAGE_PARAMS << "{"; stageParams->write( fs ); fs << "}"; - fs << CC_FEATURE_PARAMS << "{"; fs << CC_MAX_CAT_COUNT << 256; fs << CC_FEATURE_SIZE << 1; fs << "}"; + + fs << CC_FEATURE_PARAMS << "{"; + fs << CC_MAX_CAT_COUNT << featureEvaluator->getMaxCatCount(); + fs << CC_FEATURE_SIZE << featureEvaluator->getFeatureSize(); + fs << "}"; } void BrCascadeClassifier::writeFeatures( FileStorage &fs, const Mat& featureMap ) const { - compat->representation->write(fs, featureMap); + ((FeatureEvaluator*)((Ptr)featureEvaluator))->writeFeatures( fs, featureMap ); } void BrCascadeClassifier::writeStages( FileStorage &fs, const Mat& featureMap ) const @@ -285,7 +301,7 @@ void BrCascadeClassifier::save(const string filename) void BrCascadeClassifier::getUsedFeaturesIdxMap( Mat& featureMap ) { - int varCount = compat->numFeatures(); + int varCount = featureEvaluator->getNumFeatures() * featureEvaluator->getFeatureSize(); featureMap.create( 1, varCount, CV_32SC1 ); featureMap.setTo(Scalar(-1)); @@ -298,4 +314,3 @@ void BrCascadeClassifier::getUsedFeaturesIdxMap( Mat& featureMap ) featureMap.ptr(0)[fi] = idx++; } - diff --git a/openbr/core/cascade.h b/openbr/core/cascade.h index be5da8b..7708729 100644 --- a/openbr/core/cascade.h +++ b/openbr/core/cascade.h @@ -1,5 +1,5 @@ -#ifndef _CASCADE_H -#define _CASCADE_H +#ifndef CASCADE_H +#define CASCADE_H #include #include @@ -17,9 +17,6 @@ public: bool getNeg(cv::Mat &_img); bool getPos(cv::Mat &_img); -private: - bool nextNeg(); - QList posImages, negImages; int posIdx, negIdx; @@ -31,42 +28,45 @@ private: float stepFactor; size_t round; cv::Size winSize; + +private: + bool nextNeg(); }; + class BrCascadeClassifier { public: bool train(const std::string _cascadeDirName, - const QList &_posImages, - const QList &_negImages, - int _precalcValBufSize, int _precalcIdxBufSize, - int _numPos, int _numNeg, int _numStages, - Representation *_representation, - const CascadeBoostParams& _stageParams); + const QList &_posImages, + const QList &_negImages, + int _numPos, int _numNeg, + int _precalcValBufSize, int _precalcIdxBufSize, + int _numStages, + cv::Size _winSize, + const CascadeBoostParams& _stageParams); private: - int predict( int sampleIdx ); + int predict(int sampleIdx); void save(const std::string cascadeDirName); - bool updateTrainingSet( double& acceptanceRatio ); - int fillPassedSamples( int first, int count, bool isPositive, int64& consumed ); + bool updateTrainingSet(double& acceptanceRatio); + int fillPassedSamples(int first, int count, bool isPositive, int64& consumed); - void writeParams( cv::FileStorage &fs ) const; - void writeStages( cv::FileStorage &fs, const cv::Mat& featureMap ) const; - void writeFeatures( cv::FileStorage &fs, const cv::Mat& featureMap ) const; + void writeParams(cv::FileStorage &fs) const; + void writeStages(cv::FileStorage &fs, const cv::Mat& featureMap) const; + void writeFeatures(cv::FileStorage &fs, const cv::Mat& featureMap) const; - void getUsedFeaturesIdxMap( cv::Mat& featureMap ); + void getUsedFeaturesIdxMap(cv::Mat& featureMap); cv::Ptr stageParams; - cv::Ptr featureEvaluator; - cv::Ptr compat; + cv::Ptr featureEvaluator; std::vector< cv::Ptr > stageClassifiers; CascadeImageReader imgReader; - cv::Size winSize; int numStages, curNumSamples; int numPos, numNeg; + cv::Size winSize; }; } // namespace br -#endif // _CASCADE_H - +#endif // CASCADE_H diff --git a/openbr/core/features.cpp b/openbr/core/features.cpp index e07fba4..132a0a5 100644 --- a/openbr/core/features.cpp +++ b/openbr/core/features.cpp @@ -1,54 +1,57 @@ #include "features.h" -#include "opencvutils.h" using namespace cv; using namespace br; -// ------------------------------------ LBP Training ----------------------------------------------- +//------------------------------------- FeatureEvaluator --------------------------------------- -void LBPTrainingEvaluator::init(int _maxSampleCount, Size _winSize) +void FeatureEvaluator::init(int _maxSampleCount, Size _winSize ) { - CV_Assert( _maxSampleCount > 0); - sum.create((int)_maxSampleCount, (_winSize.width + 1) * (_winSize.height + 1), CV_32SC1); - + CV_Assert(_maxSampleCount > 0); winSize = _winSize; numFeatures = 0; - maxCatCount = 256; + data.create((int)_maxSampleCount, (_winSize.width + 1) * (_winSize.height + 1), CV_32SC1); cls.create( (int)_maxSampleCount, 1, CV_32FC1 ); + + maxCatCount = 256; + featSize = 1; + generateFeatures(); } -void LBPTrainingEvaluator::setImage(const Mat &img, uchar clsLabel, int idx) +void FeatureEvaluator::setImage(const Mat &img, uchar clsLabel, int idx) { - CV_DbgAssert( !sum.empty() ); + CV_Assert(img.cols == winSize.width); + CV_Assert(img.rows == winSize.height); + CV_Assert(idx < cls.rows); cls.ptr(idx)[0] = clsLabel; - Mat innSum(winSize.height + 1, winSize.width + 1, sum.type(), sum.ptr((int)idx)); - integral( img, innSum ); + Mat integralImg(winSize.height + 1, winSize.width + 1, data.type(), data.ptr(idx)); + integral(img, integralImg); } -void LBPTrainingEvaluator::writeFeatures( FileStorage &fs, const Mat& featureMap ) const +void FeatureEvaluator::writeFeatures(FileStorage &fs, const Mat &featureMap) const { _writeFeatures( features, fs, featureMap ); } -void LBPTrainingEvaluator::generateFeatures() +void FeatureEvaluator::generateFeatures() { int offset = winSize.width + 1; - for( int x = 0; x < winSize.width; x++ ) - for( int y = 0; y < winSize.height; y++ ) - for( int w = 1; w <= winSize.width / 3; w++ ) - for( int h = 1; h <= winSize.height / 3; h++ ) - if ( (x+3*w <= winSize.width) && (y+3*h <= winSize.height) ) - features.push_back( Feature(offset, x, y, w, h ) ); + for (int x = 0; x < winSize.width; x++) + for (int y = 0; y < winSize.height; y++) + for (int w = 1; w <= winSize.width / 3; w++) + for (int h = 1; h <= winSize.height / 3; h++) + if ((x+3*w <= winSize.width) && (y+3*h <= winSize.height)) + features.push_back(Feature(offset, x, y, w, h )); numFeatures = (int)features.size(); } -LBPTrainingEvaluator::Feature::Feature() +FeatureEvaluator::Feature::Feature() { rect = cvRect(0, 0, 0, 0); } -LBPTrainingEvaluator::Feature::Feature( int offset, int x, int y, int _blockWidth, int _blockHeight ) +FeatureEvaluator::Feature::Feature( int offset, int x, int y, int _blockWidth, int _blockHeight ) { Rect tr = rect = cvRect(x, y, _blockWidth, _blockHeight); CV_SUM_OFFSETS( p[0], p[1], p[4], p[5], tr, offset ) @@ -60,7 +63,7 @@ LBPTrainingEvaluator::Feature::Feature( int offset, int x, int y, int _blockWidt CV_SUM_OFFSETS( p[8], p[9], p[12], p[13], tr, offset ) } -void LBPTrainingEvaluator::Feature::write(FileStorage &fs) const +void FeatureEvaluator::Feature::write(FileStorage &fs) const { fs << CC_RECT << "[:" << rect.x << rect.y << rect.width << rect.height << "]"; } diff --git a/openbr/core/features.h b/openbr/core/features.h index a8de284..263b0f4 100644 --- a/openbr/core/features.h +++ b/openbr/core/features.h @@ -27,7 +27,7 @@ #define CC_MINHITRATE "minHitRate" #define CC_MAXFALSEALARM "maxFalseAlarm" #define CC_TRIM_RATE "weightTrimRate" -#define CC_MAX_DEPTH "maxDepth" +#define CC_MAX_DEPTH "maxDeptrh" #define CC_WEAK_COUNT "maxWeakCount" #define CC_STAGE_THRESHOLD "stageThreshold" #define CC_WEAK_CLASSIFIERS "weakClassifiers" @@ -39,24 +39,9 @@ #define CC_MAX_CAT_COUNT "maxCatCount" #define CC_FEATURE_SIZE "featSize" -#define CC_HAAR "HAAR" -#define CC_MODE "mode" -#define CC_MODE_BASIC "BASIC" -#define CC_MODE_CORE "CORE" -#define CC_MODE_ALL "ALL" -#define CC_RECTS "rects" -#define CC_TILTED "tilted" - #define CC_LBP "LBP" #define CC_RECT "rect" -#define CC_HOG "HOG" -#define CC_HOGMULTI "HOGMulti" - -#define CC_NPD "NPD" -#define CC_POINTS "points" -#define CC_POINT "point" - #ifdef _WIN32 #define TIME( arg ) (((double) clock()) / CLOCKS_PER_SEC) #else @@ -73,43 +58,6 @@ /* (x + w, y + h) */ \ (p3) = (rect).x + (rect).width + (step) * ((rect).y + (rect).height); -#define CV_TILTED_OFFSETS( p0, p1, p2, p3, rect, step ) \ - /* (x, y) */ \ - (p0) = (rect).x + (step) * (rect).y; \ - /* (x - h, y + h) */ \ - (p1) = (rect).x - (rect).height + (step) * ((rect).y + (rect).height);\ - /* (x + w, y + w) */ \ - (p2) = (rect).x + (rect).width + (step) * ((rect).y + (rect).width); \ - /* (x + w - h, y + w + h) */ \ - (p3) = (rect).x + (rect).width - (rect).height \ - + (step) * ((rect).y + (rect).width + (rect).height); - -#define CV_SUM_PTRS( p0, p1, p2, p3, sum, rect, step ) \ - /* (x, y) */ \ - (p0) = sum + (rect).x + (step) * (rect).y, \ - /* (x + w, y) */ \ - (p1) = sum + (rect).x + (rect).width + (step) * (rect).y, \ - /* (x, y + h) */ \ - (p2) = sum + (rect).x + (step) * ((rect).y + (rect).height), \ - /* (x + w, y + h) */ \ - (p3) = sum + (rect).x + (rect).width + (step) * ((rect).y + (rect).height) - -#define CV_TILTED_PTRS( p0, p1, p2, p3, tilted, rect, step ) \ - /* (x, y) */ \ - (p0) = tilted + (rect).x + (step) * (rect).y, \ - /* (x - h, y + h) */ \ - (p1) = tilted + (rect).x - (rect).height + (step) * ((rect).y + (rect).height), \ - /* (x + w, y + w) */ \ - (p2) = tilted + (rect).x + (rect).width + (step) * ((rect).y + (rect).width), \ - /* (x + w - h, y + w + h) */ \ - (p3) = tilted + (rect).x + (rect).width - (rect).height \ - + (step) * ((rect).y + (rect).width + (rect).height) - -#define CALC_SUM_(p0, p1, p2, p3, offset) \ - ((p0)[offset] - (p1)[offset] - (p2)[offset] + (p3)[offset]) - -#define CALC_SUM(rect,offset) CALC_SUM_((rect)[0], (rect)[1], (rect)[2], (rect)[3], offset) - namespace br { @@ -128,32 +76,30 @@ void _writeFeatures( const std::vector features, cv::FileStorage &fs, c fs << "]"; } -//------------------------- LBP Feature --------------------------------- - -class LBPTrainingEvaluator +class FeatureEvaluator { public: - virtual ~LBPTrainingEvaluator() {} - virtual void init(int _maxSampleCount, cv::Size _winSize ); - virtual void setImage(const cv::Mat& img, uchar clsLabel, int idx); - virtual float operator()(int featureIdx, int sampleIdx) const { return (float)features[featureIdx].calc( sum, sampleIdx); } - virtual void writeFeatures( cv::FileStorage &fs, const cv::Mat& featureMap ) const; + ~FeatureEvaluator() {} + void init(int _maxSampleCount, cv::Size _winSize); + void setImage(const cv::Mat& img, uchar clsLabel, int idx); + void writeFeatures(cv::FileStorage &fs, const cv::Mat& featureMap) const; + float operator()(int featureIdx, int sampleIdx) const { return (float)features[featureIdx].calc(data, sampleIdx); } int getNumFeatures() const { return numFeatures; } int getMaxCatCount() const { return maxCatCount; } - int getFeatureSize() const { return 1; } + int getFeatureSize() const { return featSize; } const cv::Mat& getCls() const { return cls; } float getCls(int si) const { return cls.at(si, 0); } protected: - virtual void generateFeatures(); + void generateFeatures(); class Feature { public: Feature(); Feature( int offset, int x, int y, int _block_w, int _block_h ); - uchar calc( const cv::Mat& _sum, size_t y ) const; + uchar calc( const cv::Mat &data, int y ) const; void write( cv::FileStorage &fs ) const; cv::Rect rect; @@ -161,26 +107,29 @@ protected: }; std::vector features; + cv::Mat data, cls; + int npos, nneg; - int maxCatCount; int numFeatures; + int maxCatCount; // 0 in case of numerical features + int featSize; // 1 in case of simple features (HAAR, LBP) and N_BINS(9)*N_CELLS(4) in case of Dalal's HOG features + cv::Size winSize; - cv::Mat cls, sum; }; -inline uchar LBPTrainingEvaluator::Feature::calc(const cv::Mat &_sum, size_t y) const +inline uchar FeatureEvaluator::Feature::calc(const cv::Mat &data, int y) const { - const int* psum = _sum.ptr((int)y); - int cval = psum[p[5]] - psum[p[6]] - psum[p[9]] + psum[p[10]]; - - return (uchar)((psum[p[0]] - psum[p[1]] - psum[p[4]] + psum[p[5]] >= cval ? 128 : 0) | // 0 - (psum[p[1]] - psum[p[2]] - psum[p[5]] + psum[p[6]] >= cval ? 64 : 0) | // 1 - (psum[p[2]] - psum[p[3]] - psum[p[6]] + psum[p[7]] >= cval ? 32 : 0) | // 2 - (psum[p[6]] - psum[p[7]] - psum[p[10]] + psum[p[11]] >= cval ? 16 : 0) | // 5 - (psum[p[10]] - psum[p[11]] - psum[p[14]] + psum[p[15]] >= cval ? 8 : 0) | // 8 - (psum[p[9]] - psum[p[10]] - psum[p[13]] + psum[p[14]] >= cval ? 4 : 0) | // 7 - (psum[p[8]] - psum[p[9]] - psum[p[12]] + psum[p[13]] >= cval ? 2 : 0) | // 6 - (psum[p[4]] - psum[p[5]] - psum[p[8]] + psum[p[9]] >= cval ? 1 : 0)); // 3 + const int* ptr = data.ptr(y); + int cval = ptr[p[5]] - ptr[p[6]] - ptr[p[9]] + ptr[p[10]]; + + return (uchar)((ptr[p[0]] - ptr[p[1]] - ptr[p[4]] + ptr[p[5]] >= cval ? 128 : 0) | // 0 + (ptr[p[1]] - ptr[p[2]] - ptr[p[5]] + ptr[p[6]] >= cval ? 64 : 0) | // 1 + (ptr[p[2]] - ptr[p[3]] - ptr[p[6]] + ptr[p[7]] >= cval ? 32 : 0) | // 2 + (ptr[p[6]] - ptr[p[7]] - ptr[p[10]] + ptr[p[11]] >= cval ? 16 : 0) | // 5 + (ptr[p[10]] - ptr[p[11]] - ptr[p[14]] + ptr[p[15]] >= cval ? 8 : 0) | // 8 + (ptr[p[9]] - ptr[p[10]] - ptr[p[13]] + ptr[p[14]] >= cval ? 4 : 0) | // 7 + (ptr[p[8]] - ptr[p[9]] - ptr[p[12]] + ptr[p[13]] >= cval ? 2 : 0) | // 6 + (ptr[p[4]] - ptr[p[5]] - ptr[p[8]] + ptr[p[9]] >= cval ? 1 : 0)); // 3 } } // namespace br diff --git a/openbr/plugins/metadata/cascade.cpp b/openbr/plugins/metadata/cascade.cpp index 74bea66..8787121 100644 --- a/openbr/plugins/metadata/cascade.cpp +++ b/openbr/plugins/metadata/cascade.cpp @@ -80,20 +80,18 @@ class CascadeTransform : public MetaTransform Q_PROPERTY(int winHeight READ get_winHeight WRITE set_winHeight RESET reset_winHeight STORED false) Q_PROPERTY(int numPos READ get_numPos WRITE set_numPos RESET reset_numPos STORED false) Q_PROPERTY(int numNeg READ get_numNeg WRITE set_numNeg RESET reset_numNeg STORED false) - Q_PROPERTY(int numStages READ get_numStages WRITE set_numStages RESET reset_numStages STORED false) BR_PROPERTY(QString, model, "FrontalFace") BR_PROPERTY(int, minSize, 64) BR_PROPERTY(int, minNeighbors, 5) BR_PROPERTY(bool, ROCMode, false) - BR_PROPERTY(QString, vecFile, "vec.vec") + BR_PROPERTY(QString, vecFile, "data.vec") BR_PROPERTY(QString, negFile, "neg.txt") BR_PROPERTY(int, winWidth, 24) BR_PROPERTY(int, winHeight, 24) BR_PROPERTY(int, numPos, 1000) BR_PROPERTY(int, numNeg, 1000) - BR_PROPERTY(int, numStages, 20) Resource cascadeResource; @@ -180,22 +178,21 @@ class CascadeTransform : public MetaTransform // Train transform void train(const TemplateList& data) { - (void) data; + (void)data; + + BrCascadeClassifier classifier; QList posImages = getPos(); QList negImages = getNeg(); - BrCascadeClassifier classifier; - CascadeBoostParams stageParams(CvBoost::GENTLE, 0.999, 0.5, 0.95, 1, 200); - Representation *representation = Representation::make("MBLBP(24,24)", NULL); - QString cascadeDir = Globals->sdkPath + "/share/openbr/models/openbrcascades/" + model; classifier.train(cascadeDir.toStdString(), posImages, negImages, - 1024, 1024, numPos, numNeg, numStages, - representation, stageParams); + numPos, numNeg, 1024, 1024, 12, + Size(winWidth, winHeight), + stageParams); } void project(const Template &src, Template &dst) const diff --git a/openbr/plugins/representation/mblbp.cpp b/openbr/plugins/representation/mblbp.cpp index 53aad12..5fc7f0e 100644 --- a/openbr/plugins/representation/mblbp.cpp +++ b/openbr/plugins/representation/mblbp.cpp @@ -1,6 +1,7 @@ #include #include +#include using namespace cv; @@ -29,12 +30,12 @@ class MBLBPRepresentation : public Representation void init() { int offset = winWidth + 1; - for( int x = 0; x < winWidth; x++ ) - for( int y = 0; y < winHeight; y++ ) - for( int w = 1; w <= winWidth / 3; w++ ) - for( int h = 1; h <= winHeight / 3; h++ ) - if ( (x+3*w <= winWidth) && (y+3*h <= winHeight) ) - features.push_back( Feature(offset, x, y, w, h ) ); + for (int x = 0; x < winWidth; x++ ) + for (int y = 0; y < winHeight; y++ ) + for (int w = 1; w <= winWidth / 3; w++ ) + for (int h = 1; h <= winHeight / 3; h++ ) + if ((x+3*w <= winWidth) && (y+3*h <= winHeight) ) + features.append(Feature(offset, x, y, w, h ) ); } Mat preprocess(const Mat &image) const -- libgit2 0.21.4