571 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			571 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #include "opencv2/core/core.hpp"
 | |
| #include "opencv2/core/internal.hpp"
 | |
| 
 | |
| #include "cascadeclassifier.h"
 | |
| #include <queue>
 | |
| 
 | |
| using namespace std;
 | |
| using namespace cv;
 | |
| 
 | |
| static const char* stageTypes[] = { CC_BOOST };
 | |
| static const char* featureTypes[] = { CC_HAAR, CC_LBP, CC_HOG };
 | |
| 
 | |
| CvCascadeParams::CvCascadeParams() : stageType( defaultStageType ),
 | |
|     featureType( defaultFeatureType ), winSize( cvSize(24, 24) )
 | |
| {
 | |
|     name = CC_CASCADE_PARAMS;
 | |
| }
 | |
| CvCascadeParams::CvCascadeParams( int _stageType, int _featureType ) : stageType( _stageType ),
 | |
|     featureType( _featureType ), winSize( cvSize(24, 24) )
 | |
| {
 | |
|     name = CC_CASCADE_PARAMS;
 | |
| }
 | |
| 
 | |
| //---------------------------- CascadeParams --------------------------------------
 | |
| 
 | |
| void CvCascadeParams::write( FileStorage &fs ) const
 | |
| {
 | |
|     string stageTypeStr = stageType == BOOST ? CC_BOOST : string();
 | |
|     CV_Assert( !stageTypeStr.empty() );
 | |
|     fs << CC_STAGE_TYPE << stageTypeStr;
 | |
|     string featureTypeStr = featureType == CvFeatureParams::HAAR ? CC_HAAR :
 | |
|                             featureType == CvFeatureParams::LBP ? CC_LBP :
 | |
|                             featureType == CvFeatureParams::HOG ? CC_HOG :
 | |
|                             0;
 | |
|     CV_Assert( !stageTypeStr.empty() );
 | |
|     fs << CC_FEATURE_TYPE << featureTypeStr;
 | |
|     fs << CC_HEIGHT << winSize.height;
 | |
|     fs << CC_WIDTH << winSize.width;
 | |
| }
 | |
| 
 | |
| bool CvCascadeParams::read( const FileNode &node )
 | |
| {
 | |
|     if ( node.empty() )
 | |
|         return false;
 | |
|     string stageTypeStr, featureTypeStr;
 | |
|     FileNode rnode = node[CC_STAGE_TYPE];
 | |
|     if ( !rnode.isString() )
 | |
|         return false;
 | |
|     rnode >> stageTypeStr;
 | |
|     stageType = !stageTypeStr.compare( CC_BOOST ) ? BOOST : -1;
 | |
|     if (stageType == -1)
 | |
|         return false;
 | |
|     rnode = node[CC_FEATURE_TYPE];
 | |
|     if ( !rnode.isString() )
 | |
|         return false;
 | |
|     rnode >> featureTypeStr;
 | |
|     featureType = !featureTypeStr.compare( CC_HAAR ) ? CvFeatureParams::HAAR :
 | |
|                   !featureTypeStr.compare( CC_LBP ) ? CvFeatureParams::LBP :
 | |
|                   !featureTypeStr.compare( CC_HOG ) ? CvFeatureParams::HOG :
 | |
|                   -1;
 | |
|     if (featureType == -1)
 | |
|         return false;
 | |
|     node[CC_HEIGHT] >> winSize.height;
 | |
|     node[CC_WIDTH] >> winSize.width;
 | |
|     return winSize.height > 0 && winSize.width > 0;
 | |
| }
 | |
| 
 | |
| void CvCascadeParams::printDefaults() const
 | |
| {
 | |
|     CvParams::printDefaults();
 | |
|     cout << "  [-stageType <";
 | |
|     for( int i = 0; i < (int)(sizeof(stageTypes)/sizeof(stageTypes[0])); i++ )
 | |
|     {
 | |
|         cout << (i ? " | " : "") << stageTypes[i];
 | |
|         if ( i == defaultStageType )
 | |
|             cout << "(default)";
 | |
|     }
 | |
|     cout << ">]" << endl;
 | |
| 
 | |
|     cout << "  [-featureType <{";
 | |
|     for( int i = 0; i < (int)(sizeof(featureTypes)/sizeof(featureTypes[0])); i++ )
 | |
|     {
 | |
|         cout << (i ? ", " : "") << featureTypes[i];
 | |
|         if ( i == defaultStageType )
 | |
|             cout << "(default)";
 | |
|     }
 | |
|     cout << "}>]" << endl;
 | |
|     cout << "  [-w <sampleWidth = " << winSize.width << ">]" << endl;
 | |
|     cout << "  [-h <sampleHeight = " << winSize.height << ">]" << endl;
 | |
| }
 | |
| 
 | |
| void CvCascadeParams::printAttrs() const
 | |
| {
 | |
|     cout << "stageType: " << stageTypes[stageType] << endl;
 | |
|     cout << "featureType: " << featureTypes[featureType] << endl;
 | |
|     cout << "sampleWidth: " << winSize.width << endl;
 | |
|     cout << "sampleHeight: " << winSize.height << endl;
 | |
| }
 | |
| 
 | |
| bool CvCascadeParams::scanAttr( const string prmName, const string val )
 | |
| {
 | |
|     bool res = true;
 | |
|     if( !prmName.compare( "-stageType" ) )
 | |
|     {
 | |
|         for( int i = 0; i < (int)(sizeof(stageTypes)/sizeof(stageTypes[0])); i++ )
 | |
|             if( !val.compare( stageTypes[i] ) )
 | |
|                 stageType = i;
 | |
|     }
 | |
|     else if( !prmName.compare( "-featureType" ) )
 | |
|     {
 | |
|         for( int i = 0; i < (int)(sizeof(featureTypes)/sizeof(featureTypes[0])); i++ )
 | |
|             if( !val.compare( featureTypes[i] ) )
 | |
|                 featureType = i;
 | |
|     }
 | |
|     else if( !prmName.compare( "-w" ) )
 | |
|     {
 | |
|         winSize.width = atoi( val.c_str() );
 | |
|     }
 | |
|     else if( !prmName.compare( "-h" ) )
 | |
|     {
 | |
|         winSize.height = atoi( val.c_str() );
 | |
|     }
 | |
|     else
 | |
|         res = false;
 | |
|     return res;
 | |
| }
 | |
| 
 | |
| //---------------------------- CascadeClassifier --------------------------------------
 | |
| 
 | |
| bool CvCascadeClassifier::train( const string _cascadeDirName,
 | |
|                                 const string _posFilename,
 | |
|                                 const string _negFilename,
 | |
|                                 int _numPos, int _numNeg,
 | |
|                                 int _precalcValBufSize, int _precalcIdxBufSize,
 | |
|                                 int _numStages,
 | |
|                                 const CvCascadeParams& _cascadeParams,
 | |
|                                 const CvFeatureParams& _featureParams,
 | |
|                                 const CvCascadeBoostParams& _stageParams,
 | |
|                                 bool baseFormatSave,
 | |
|                                 double acceptanceRatioBreakValue)
 | |
| {
 | |
|     // Start recording clock ticks for training time output
 | |
|     const clock_t begin_time = clock();
 | |
| 
 | |
|     if( _cascadeDirName.empty() || _posFilename.empty() || _negFilename.empty() )
 | |
|         CV_Error( CV_StsBadArg, "_cascadeDirName or _bgfileName or _vecFileName is NULL" );
 | |
| 
 | |
|     string dirName;
 | |
|     if (_cascadeDirName.find_last_of("/\\") == (_cascadeDirName.length() - 1) )
 | |
|         dirName = _cascadeDirName;
 | |
|     else
 | |
|         dirName = _cascadeDirName + '/';
 | |
| 
 | |
|     numPos = _numPos;
 | |
|     numNeg = _numNeg;
 | |
|     numStages = _numStages;
 | |
|     if ( !imgReader.create( _posFilename, _negFilename, _cascadeParams.winSize ) )
 | |
|     {
 | |
|         cout << "Image reader can not be created from -vec " << _posFilename
 | |
|                 << " and -bg " << _negFilename << "." << endl;
 | |
|         return false;
 | |
|     }
 | |
|     if ( !load( dirName ) )
 | |
|     {
 | |
|         cascadeParams = _cascadeParams;
 | |
|         featureParams = CvFeatureParams::create(cascadeParams.featureType);
 | |
|         featureParams->init(_featureParams);
 | |
|         stageParams = new CvCascadeBoostParams;
 | |
|         *stageParams = _stageParams;
 | |
|         featureEvaluator = CvFeatureEvaluator::create(cascadeParams.featureType);
 | |
|         featureEvaluator->init( (CvFeatureParams*)featureParams, numPos + numNeg, cascadeParams.winSize );
 | |
|         stageClassifiers.reserve( numStages );
 | |
|     }else{
 | |
|         // Make sure that if model parameters are preloaded, that people are aware of this,
 | |
|         // even when passing other parameters to the training command
 | |
|         cout << "---------------------------------------------------------------------------------" << endl;
 | |
|         cout << "Training parameters are pre-loaded from the parameter file in data folder!" << endl;
 | |
|         cout << "Please empty this folder if you want to use a NEW set of training parameters." << endl;
 | |
|         cout << "---------------------------------------------------------------------------------" << endl;
 | |
|     }
 | |
|     cout << "PARAMETERS:" << endl;
 | |
|     cout << "cascadeDirName: " << _cascadeDirName << endl;
 | |
|     cout << "vecFileName: " << _posFilename << endl;
 | |
|     cout << "bgFileName: " << _negFilename << endl;
 | |
|     cout << "numPos: " << _numPos << endl;
 | |
|     cout << "numNeg: " << _numNeg << endl;
 | |
|     cout << "numStages: " << numStages << endl;
 | |
|     cout << "precalcValBufSize[Mb] : " << _precalcValBufSize << endl;
 | |
|     cout << "precalcIdxBufSize[Mb] : " << _precalcIdxBufSize << endl;
 | |
|     cout << "acceptanceRatioBreakValue : " << acceptanceRatioBreakValue << endl;
 | |
|     cascadeParams.printAttrs();
 | |
|     stageParams->printAttrs();
 | |
|     featureParams->printAttrs();
 | |
| 
 | |
|     int startNumStages = (int)stageClassifiers.size();
 | |
|     if ( startNumStages > 1 )
 | |
|         cout << endl << "Stages 0-" << startNumStages-1 << " are loaded" << endl;
 | |
|     else if ( startNumStages == 1)
 | |
|         cout << endl << "Stage 0 is loaded" << endl;
 | |
| 
 | |
|     double requiredLeafFARate = pow( (double) stageParams->maxFalseAlarm, (double) numStages ) /
 | |
|                                 (double)stageParams->max_depth;
 | |
|     double tempLeafFARate;
 | |
| 
 | |
|     for( int i = startNumStages; i < numStages; i++ )
 | |
|     {
 | |
|         cout << endl << "===== TRAINING " << i << "-stage =====" << endl;
 | |
|         cout << "<BEGIN" << endl;
 | |
| 
 | |
|         if ( !updateTrainingSet( requiredLeafFARate, tempLeafFARate ) )
 | |
|         {
 | |
|             cout << "Train dataset for temp stage can not be filled. "
 | |
|                     "Branch training terminated." << endl;
 | |
|             break;
 | |
|         }
 | |
|         if( tempLeafFARate <= requiredLeafFARate )
 | |
|         {
 | |
|             cout << "Required leaf false alarm rate achieved. "
 | |
|                     "Branch training terminated." << endl;
 | |
|             break;
 | |
|         }
 | |
|         if( (tempLeafFARate <= acceptanceRatioBreakValue) && (acceptanceRatioBreakValue >= 0) ){
 | |
|             cout << "The required acceptanceRatio for the model has been reached to avoid overfitting of trainingdata. "
 | |
|                     "Branch training terminated." << endl;
 | |
|             break;
 | |
| }
 | |
| 
 | |
|         CvCascadeBoost* tempStage = new CvCascadeBoost;
 | |
|         bool isStageTrained = tempStage->train( (CvFeatureEvaluator*)featureEvaluator,
 | |
|                                                 curNumSamples, _precalcValBufSize, _precalcIdxBufSize,
 | |
|                                                 *((CvCascadeBoostParams*)stageParams) );
 | |
|         cout << "END>" << endl;
 | |
| 
 | |
|         if(!isStageTrained)
 | |
|             break;
 | |
| 
 | |
|         stageClassifiers.push_back( tempStage );
 | |
| 
 | |
|         // save params
 | |
|         if( i == 0)
 | |
|         {
 | |
|             std::string paramsFilename = dirName + CC_PARAMS_FILENAME;
 | |
|             FileStorage fs( paramsFilename, FileStorage::WRITE);
 | |
|             if ( !fs.isOpened() )
 | |
|             {
 | |
|                 cout << "Parameters can not be written, because file " << paramsFilename
 | |
|                         << " can not be opened." << endl;
 | |
|                 return false;
 | |
|             }
 | |
|             fs << FileStorage::getDefaultObjectName(paramsFilename) << "{";
 | |
|             writeParams( fs );
 | |
|             fs << "}";
 | |
|         }
 | |
|         // save current stage
 | |
|         char buf[10];
 | |
|         sprintf(buf, "%s%d", "stage", i );
 | |
|         string stageFilename = dirName + buf + ".xml";
 | |
|         FileStorage fs( stageFilename, FileStorage::WRITE );
 | |
|         if ( !fs.isOpened() )
 | |
|         {
 | |
|             cout << "Current stage can not be written, because file " << stageFilename
 | |
|                     << " can not be opened." << endl;
 | |
|             return false;
 | |
|         }
 | |
|         fs << FileStorage::getDefaultObjectName(stageFilename) << "{";
 | |
|         tempStage->write( fs, Mat() );
 | |
|         fs << "}";
 | |
| 
 | |
|         // Output training time up till now
 | |
|         float seconds = float( clock () - begin_time ) / CLOCKS_PER_SEC;
 | |
|         int days = int(seconds) / 60 / 60 / 24;
 | |
|         int hours = (int(seconds) / 60 / 60) % 24;
 | |
|         int minutes = (int(seconds) / 60) % 60;
 | |
|         int seconds_left = int(seconds) % 60;
 | |
|         cout << "Training until now has taken " << days << " days " << hours << " hours " << minutes << " minutes " << seconds_left <<" seconds." << endl;
 | |
|     }
 | |
| 
 | |
|     if(stageClassifiers.size() == 0)
 | |
|     {
 | |
|         cout << "Cascade classifier can't be trained. Check the used training parameters." << endl;
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     save( dirName + CC_CASCADE_FILENAME, baseFormatSave );
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| int CvCascadeClassifier::predict( int sampleIdx )
 | |
| {
 | |
|     CV_DbgAssert( sampleIdx < numPos + numNeg );
 | |
|     for (vector< Ptr<CvCascadeBoost> >::iterator it = stageClassifiers.begin();
 | |
|         it != stageClassifiers.end(); it++ )
 | |
|     {
 | |
|         if ( (*it)->predict( sampleIdx ) == 0.f )
 | |
|             return 0;
 | |
|     }
 | |
|     return 1;
 | |
| }
 | |
| 
 | |
| bool CvCascadeClassifier::updateTrainingSet( double minimumAcceptanceRatio, double& acceptanceRatio)
 | |
| {
 | |
|     int64 posConsumed = 0, negConsumed = 0;
 | |
|     imgReader.restart();
 | |
|     int posCount = fillPassedSamples( 0, numPos, true, 0, posConsumed );
 | |
|     if( !posCount )
 | |
|         return false;
 | |
|     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, minimumAcceptanceRatio, negConsumed );
 | |
|     if ( !negCount )
 | |
|         return false;
 | |
| 
 | |
|     curNumSamples = posCount + negCount;
 | |
|     acceptanceRatio = negConsumed == 0 ? 0 : ( (double)negCount/(double)(int64)negConsumed );
 | |
|     cout << "NEG count : acceptanceRatio    " << negCount << " : " << acceptanceRatio << endl;
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| int CvCascadeClassifier::fillPassedSamples( int first, int count, bool isPositive, double minimumAcceptanceRatio, int64& consumed )
 | |
| {
 | |
|     int getcount = 0;
 | |
|     Mat img(cascadeParams.winSize, CV_8UC1);
 | |
|     for( int i = first; i < first + count; i++ )
 | |
|     {
 | |
|         for( ; ; )
 | |
|         {
 | |
|             if( consumed != 0 && ((double)getcount+1)/(double)(int64)consumed <= minimumAcceptanceRatio )
 | |
|                 return getcount;
 | |
| 
 | |
|             bool isGetImg = isPositive ? imgReader.getPos( img ) :
 | |
|                                            imgReader.getNeg( img );
 | |
|             if( !isGetImg )
 | |
|                 return getcount;
 | |
|             consumed++;
 | |
| 
 | |
|             featureEvaluator->setImage( img, isPositive ? 1 : 0, i );
 | |
|             if( predict( i ) == 1.0F )
 | |
|             {
 | |
|                 getcount++;
 | |
|                 printf("%s current samples: %d\r", isPositive ? "POS":"NEG", getcount);
 | |
|                 break;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     return getcount;
 | |
| }
 | |
| 
 | |
| void CvCascadeClassifier::writeParams( FileStorage &fs ) const
 | |
| {
 | |
|     cascadeParams.write( fs );
 | |
|     fs << CC_STAGE_PARAMS << "{"; stageParams->write( fs ); fs << "}";
 | |
|     fs << CC_FEATURE_PARAMS << "{"; featureParams->write( fs ); fs << "}";
 | |
| }
 | |
| 
 | |
| void CvCascadeClassifier::writeFeatures( FileStorage &fs, const Mat& featureMap ) const
 | |
| {
 | |
|     ((CvFeatureEvaluator*)((Ptr<CvFeatureEvaluator>)featureEvaluator))->writeFeatures( fs, featureMap );
 | |
| }
 | |
| 
 | |
| void CvCascadeClassifier::writeStages( FileStorage &fs, const Mat& featureMap ) const
 | |
| {
 | |
|     char cmnt[30];
 | |
|     int i = 0;
 | |
|     fs << CC_STAGES << "[";
 | |
|     for( vector< Ptr<CvCascadeBoost> >::const_iterator it = stageClassifiers.begin();
 | |
|         it != stageClassifiers.end(); it++, i++ )
 | |
|     {
 | |
|         sprintf( cmnt, "stage %d", i );
 | |
|         cvWriteComment( fs.fs, cmnt, 0 );
 | |
|         fs << "{";
 | |
|         ((CvCascadeBoost*)((Ptr<CvCascadeBoost>)*it))->write( fs, featureMap );
 | |
|         fs << "}";
 | |
|     }
 | |
|     fs << "]";
 | |
| }
 | |
| 
 | |
| bool CvCascadeClassifier::readParams( const FileNode &node )
 | |
| {
 | |
|     if ( !node.isMap() || !cascadeParams.read( node ) )
 | |
|         return false;
 | |
| 
 | |
|     stageParams = new CvCascadeBoostParams;
 | |
|     FileNode rnode = node[CC_STAGE_PARAMS];
 | |
|     if ( !stageParams->read( rnode ) )
 | |
|         return false;
 | |
| 
 | |
|     featureParams = CvFeatureParams::create(cascadeParams.featureType);
 | |
|     rnode = node[CC_FEATURE_PARAMS];
 | |
|     if ( !featureParams->read( rnode ) )
 | |
|         return false;
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| bool CvCascadeClassifier::readStages( const FileNode &node)
 | |
| {
 | |
|     FileNode rnode = node[CC_STAGES];
 | |
|     if (!rnode.empty() || !rnode.isSeq())
 | |
|         return false;
 | |
|     stageClassifiers.reserve(numStages);
 | |
|     FileNodeIterator it = rnode.begin();
 | |
|     for( int i = 0; i < min( (int)rnode.size(), numStages ); i++, it++ )
 | |
|     {
 | |
|         CvCascadeBoost* tempStage = new CvCascadeBoost;
 | |
|         if ( !tempStage->read( *it, (CvFeatureEvaluator *)featureEvaluator, *((CvCascadeBoostParams*)stageParams) ) )
 | |
|         {
 | |
|             delete tempStage;
 | |
|             return false;
 | |
|         }
 | |
|         stageClassifiers.push_back(tempStage);
 | |
|     }
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| // For old Haar Classifier file saving
 | |
| #define ICV_HAAR_SIZE_NAME            "size"
 | |
| #define ICV_HAAR_STAGES_NAME          "stages"
 | |
| #define ICV_HAAR_TREES_NAME             "trees"
 | |
| #define ICV_HAAR_FEATURE_NAME             "feature"
 | |
| #define ICV_HAAR_RECTS_NAME                 "rects"
 | |
| #define ICV_HAAR_TILTED_NAME                "tilted"
 | |
| #define ICV_HAAR_THRESHOLD_NAME           "threshold"
 | |
| #define ICV_HAAR_LEFT_NODE_NAME           "left_node"
 | |
| #define ICV_HAAR_LEFT_VAL_NAME            "left_val"
 | |
| #define ICV_HAAR_RIGHT_NODE_NAME          "right_node"
 | |
| #define ICV_HAAR_RIGHT_VAL_NAME           "right_val"
 | |
| #define ICV_HAAR_STAGE_THRESHOLD_NAME   "stage_threshold"
 | |
| #define ICV_HAAR_PARENT_NAME            "parent"
 | |
| #define ICV_HAAR_NEXT_NAME              "next"
 | |
| 
 | |
| void CvCascadeClassifier::save( const string filename, bool baseFormat )
 | |
| {
 | |
|     FileStorage fs( filename, FileStorage::WRITE );
 | |
| 
 | |
|     if ( !fs.isOpened() )
 | |
|         return;
 | |
| 
 | |
|     fs << FileStorage::getDefaultObjectName(filename) << "{";
 | |
|     if ( !baseFormat )
 | |
|     {
 | |
|         Mat featureMap;
 | |
|         getUsedFeaturesIdxMap( featureMap );
 | |
|         writeParams( fs );
 | |
|         fs << CC_STAGE_NUM << (int)stageClassifiers.size();
 | |
|         writeStages( fs, featureMap );
 | |
|         writeFeatures( fs, featureMap );
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         //char buf[256];
 | |
|         CvSeq* weak;
 | |
|         if ( cascadeParams.featureType != CvFeatureParams::HAAR )
 | |
|             CV_Error( CV_StsBadFunc, "old file format is used for Haar-like features only");
 | |
|         fs << ICV_HAAR_SIZE_NAME << "[:" << cascadeParams.winSize.width <<
 | |
|             cascadeParams.winSize.height << "]";
 | |
|         fs << ICV_HAAR_STAGES_NAME << "[";
 | |
|         for( size_t si = 0; si < stageClassifiers.size(); si++ )
 | |
|         {
 | |
|             fs << "{"; //stage
 | |
|             /*sprintf( buf, "stage %d", si );
 | |
|             CV_CALL( cvWriteComment( fs, buf, 1 ) );*/
 | |
|             weak = stageClassifiers[si]->get_weak_predictors();
 | |
|             fs << ICV_HAAR_TREES_NAME << "[";
 | |
|             for( int wi = 0; wi < weak->total; wi++ )
 | |
|             {
 | |
|                 int inner_node_idx = -1, total_inner_node_idx = -1;
 | |
|                 queue<const CvDTreeNode*> inner_nodes_queue;
 | |
|                 CvCascadeBoostTree* tree = *((CvCascadeBoostTree**) cvGetSeqElem( weak, wi ));
 | |
| 
 | |
|                 fs << "[";
 | |
|                 /*sprintf( buf, "tree %d", wi );
 | |
|                 CV_CALL( cvWriteComment( fs, buf, 1 ) );*/
 | |
| 
 | |
|                 const CvDTreeNode* tempNode;
 | |
| 
 | |
|                 inner_nodes_queue.push( tree->get_root() );
 | |
|                 total_inner_node_idx++;
 | |
| 
 | |
|                 while (!inner_nodes_queue.empty())
 | |
|                 {
 | |
|                     tempNode = inner_nodes_queue.front();
 | |
|                     inner_node_idx++;
 | |
| 
 | |
|                     fs << "{";
 | |
|                     fs << ICV_HAAR_FEATURE_NAME << "{";
 | |
|                     ((CvHaarEvaluator*)((CvFeatureEvaluator*)featureEvaluator))->writeFeature( fs, tempNode->split->var_idx );
 | |
|                     fs << "}";
 | |
| 
 | |
|                     fs << ICV_HAAR_THRESHOLD_NAME << tempNode->split->ord.c;
 | |
| 
 | |
|                     if( tempNode->left->left || tempNode->left->right )
 | |
|                     {
 | |
|                         inner_nodes_queue.push( tempNode->left );
 | |
|                         total_inner_node_idx++;
 | |
|                         fs << ICV_HAAR_LEFT_NODE_NAME << total_inner_node_idx;
 | |
|                     }
 | |
|                     else
 | |
|                         fs << ICV_HAAR_LEFT_VAL_NAME << tempNode->left->value;
 | |
| 
 | |
|                     if( tempNode->right->left || tempNode->right->right )
 | |
|                     {
 | |
|                         inner_nodes_queue.push( tempNode->right );
 | |
|                         total_inner_node_idx++;
 | |
|                         fs << ICV_HAAR_RIGHT_NODE_NAME << total_inner_node_idx;
 | |
|                     }
 | |
|                     else
 | |
|                         fs << ICV_HAAR_RIGHT_VAL_NAME << tempNode->right->value;
 | |
|                     fs << "}"; // ICV_HAAR_FEATURE_NAME
 | |
|                     inner_nodes_queue.pop();
 | |
|                 }
 | |
|                 fs << "]";
 | |
|             }
 | |
|             fs << "]"; //ICV_HAAR_TREES_NAME
 | |
|             fs << ICV_HAAR_STAGE_THRESHOLD_NAME << stageClassifiers[si]->getThreshold();
 | |
|             fs << ICV_HAAR_PARENT_NAME << (int)si-1 << ICV_HAAR_NEXT_NAME << -1;
 | |
|             fs << "}"; //stage
 | |
|         } /* for each stage */
 | |
|         fs << "]"; //ICV_HAAR_STAGES_NAME
 | |
|     }
 | |
|     fs << "}";
 | |
| }
 | |
| 
 | |
| bool CvCascadeClassifier::load( const string cascadeDirName )
 | |
| {
 | |
|     FileStorage fs( cascadeDirName + CC_PARAMS_FILENAME, FileStorage::READ );
 | |
|     if ( !fs.isOpened() )
 | |
|         return false;
 | |
|     FileNode node = fs.getFirstTopLevelNode();
 | |
|     if ( !readParams( node ) )
 | |
|         return false;
 | |
|     featureEvaluator = CvFeatureEvaluator::create(cascadeParams.featureType);
 | |
|     featureEvaluator->init( ((CvFeatureParams*)featureParams), numPos + numNeg, cascadeParams.winSize );
 | |
|     fs.release();
 | |
| 
 | |
|     char buf[10];
 | |
|     for ( int si = 0; si < numStages; si++ )
 | |
|     {
 | |
|         sprintf( buf, "%s%d", "stage", si);
 | |
|         fs.open( cascadeDirName + buf + ".xml", FileStorage::READ );
 | |
|         node = fs.getFirstTopLevelNode();
 | |
|         if ( !fs.isOpened() )
 | |
|             break;
 | |
|         CvCascadeBoost *tempStage = new CvCascadeBoost;
 | |
| 
 | |
|         if ( !tempStage->read( node, (CvFeatureEvaluator*)featureEvaluator, *((CvCascadeBoostParams*)stageParams )) )
 | |
|         {
 | |
|             delete tempStage;
 | |
|             fs.release();
 | |
|             break;
 | |
|         }
 | |
|         stageClassifiers.push_back(tempStage);
 | |
|     }
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| void CvCascadeClassifier::getUsedFeaturesIdxMap( Mat& featureMap )
 | |
| {
 | |
|     int varCount = featureEvaluator->getNumFeatures() * featureEvaluator->getFeatureSize();
 | |
|     featureMap.create( 1, varCount, CV_32SC1 );
 | |
|     featureMap.setTo(Scalar(-1));
 | |
| 
 | |
|     for( vector< Ptr<CvCascadeBoost> >::const_iterator it = stageClassifiers.begin();
 | |
|         it != stageClassifiers.end(); it++ )
 | |
|         ((CvCascadeBoost*)((Ptr<CvCascadeBoost>)(*it)))->markUsedFeaturesInMap( featureMap );
 | |
| 
 | |
|     for( int fi = 0, idx = 0; fi < varCount; fi++ )
 | |
|         if ( featureMap.at<int>(0, fi) >= 0 )
 | |
|             featureMap.ptr<int>(0)[fi] = idx++;
 | |
| }
 | 
