1 #include "opencv2/core/core.hpp"
2 #include "opencv2/core/internal.hpp"
4 #include "cascadeclassifier.h"
10 static const char* stageTypes[] = { CC_BOOST };
11 static const char* featureTypes[] = { CC_HAAR, CC_LBP, CC_HOG };
13 CvCascadeParams::CvCascadeParams() : stageType( defaultStageType ),
14 featureType( defaultFeatureType ), winSize( cvSize(24, 24) )
16 name = CC_CASCADE_PARAMS;
18 CvCascadeParams::CvCascadeParams( int _stageType, int _featureType ) : stageType( _stageType ),
19 featureType( _featureType ), winSize( cvSize(24, 24) )
21 name = CC_CASCADE_PARAMS;
24 //---------------------------- CascadeParams --------------------------------------
26 void CvCascadeParams::write( FileStorage &fs ) const
28 string stageTypeStr = stageType == BOOST ? CC_BOOST : string();
29 CV_Assert( !stageTypeStr.empty() );
30 fs << CC_STAGE_TYPE << stageTypeStr;
31 string featureTypeStr = featureType == CvFeatureParams::HAAR ? CC_HAAR :
32 featureType == CvFeatureParams::LBP ? CC_LBP :
33 featureType == CvFeatureParams::HOG ? CC_HOG :
35 CV_Assert( !stageTypeStr.empty() );
36 fs << CC_FEATURE_TYPE << featureTypeStr;
37 fs << CC_HEIGHT << winSize.height;
38 fs << CC_WIDTH << winSize.width;
41 bool CvCascadeParams::read( const FileNode &node )
45 string stageTypeStr, featureTypeStr;
46 FileNode rnode = node[CC_STAGE_TYPE];
47 if ( !rnode.isString() )
49 rnode >> stageTypeStr;
50 stageType = !stageTypeStr.compare( CC_BOOST ) ? BOOST : -1;
53 rnode = node[CC_FEATURE_TYPE];
54 if ( !rnode.isString() )
56 rnode >> featureTypeStr;
57 featureType = !featureTypeStr.compare( CC_HAAR ) ? CvFeatureParams::HAAR :
58 !featureTypeStr.compare( CC_LBP ) ? CvFeatureParams::LBP :
59 !featureTypeStr.compare( CC_HOG ) ? CvFeatureParams::HOG :
61 if (featureType == -1)
63 node[CC_HEIGHT] >> winSize.height;
64 node[CC_WIDTH] >> winSize.width;
65 return winSize.height > 0 && winSize.width > 0;
68 void CvCascadeParams::printDefaults() const
70 CvParams::printDefaults();
71 cout << " [-stageType <";
72 for( int i = 0; i < (int)(sizeof(stageTypes)/sizeof(stageTypes[0])); i++ )
74 cout << (i ? " | " : "") << stageTypes[i];
75 if ( i == defaultStageType )
80 cout << " [-featureType <{";
81 for( int i = 0; i < (int)(sizeof(featureTypes)/sizeof(featureTypes[0])); i++ )
83 cout << (i ? ", " : "") << featureTypes[i];
84 if ( i == defaultStageType )
87 cout << "}>]" << endl;
88 cout << " [-w <sampleWidth = " << winSize.width << ">]" << endl;
89 cout << " [-h <sampleHeight = " << winSize.height << ">]" << endl;
92 void CvCascadeParams::printAttrs() const
94 cout << "stageType: " << stageTypes[stageType] << endl;
95 cout << "featureType: " << featureTypes[featureType] << endl;
96 cout << "sampleWidth: " << winSize.width << endl;
97 cout << "sampleHeight: " << winSize.height << endl;
100 bool CvCascadeParams::scanAttr( const string prmName, const string val )
103 if( !prmName.compare( "-stageType" ) )
105 for( int i = 0; i < (int)(sizeof(stageTypes)/sizeof(stageTypes[0])); i++ )
106 if( !val.compare( stageTypes[i] ) )
109 else if( !prmName.compare( "-featureType" ) )
111 for( int i = 0; i < (int)(sizeof(featureTypes)/sizeof(featureTypes[0])); i++ )
112 if( !val.compare( featureTypes[i] ) )
115 else if( !prmName.compare( "-w" ) )
117 winSize.width = atoi( val.c_str() );
119 else if( !prmName.compare( "-h" ) )
121 winSize.height = atoi( val.c_str() );
128 //---------------------------- CascadeClassifier --------------------------------------
130 bool CvCascadeClassifier::train( const string _cascadeDirName,
131 const string _posFilename,
132 const string _negFilename,
133 int _numPos, int _numNeg,
134 int _precalcValBufSize, int _precalcIdxBufSize,
136 const CvCascadeParams& _cascadeParams,
137 const CvFeatureParams& _featureParams,
138 const CvCascadeBoostParams& _stageParams,
139 bool baseFormatSave )
141 // Start recording clock ticks for training time output
142 const clock_t begin_time = clock();
144 if( _cascadeDirName.empty() || _posFilename.empty() || _negFilename.empty() )
145 CV_Error( CV_StsBadArg, "_cascadeDirName or _bgfileName or _vecFileName is NULL" );
148 if (_cascadeDirName.find_last_of("/\\") == (_cascadeDirName.length() - 1) )
149 dirName = _cascadeDirName;
151 dirName = _cascadeDirName + '/';
155 numStages = _numStages;
156 if ( !imgReader.create( _posFilename, _negFilename, _cascadeParams.winSize ) )
158 cout << "Image reader can not be created from -vec " << _posFilename
159 << " and -bg " << _negFilename << "." << endl;
162 if ( !load( dirName ) )
164 cascadeParams = _cascadeParams;
165 featureParams = CvFeatureParams::create(cascadeParams.featureType);
166 featureParams->init(_featureParams);
167 stageParams = new CvCascadeBoostParams;
168 *stageParams = _stageParams;
169 featureEvaluator = CvFeatureEvaluator::create(cascadeParams.featureType);
170 featureEvaluator->init( (CvFeatureParams*)featureParams, numPos + numNeg, cascadeParams.winSize );
171 stageClassifiers.reserve( numStages );
173 cout << "PARAMETERS:" << endl;
174 cout << "cascadeDirName: " << _cascadeDirName << endl;
175 cout << "vecFileName: " << _posFilename << endl;
176 cout << "bgFileName: " << _negFilename << endl;
177 cout << "numPos: " << _numPos << endl;
178 cout << "numNeg: " << _numNeg << endl;
179 cout << "numStages: " << numStages << endl;
180 cout << "precalcValBufSize[Mb] : " << _precalcValBufSize << endl;
181 cout << "precalcIdxBufSize[Mb] : " << _precalcIdxBufSize << endl;
182 cascadeParams.printAttrs();
183 stageParams->printAttrs();
184 featureParams->printAttrs();
186 int startNumStages = (int)stageClassifiers.size();
187 if ( startNumStages > 1 )
188 cout << endl << "Stages 0-" << startNumStages-1 << " are loaded" << endl;
189 else if ( startNumStages == 1)
190 cout << endl << "Stage 0 is loaded" << endl;
192 double requiredLeafFARate = pow( (double) stageParams->maxFalseAlarm, (double) numStages ) /
193 (double)stageParams->max_depth;
194 double tempLeafFARate;
196 for( int i = startNumStages; i < numStages; i++ )
198 cout << endl << "===== TRAINING " << i << "-stage =====" << endl;
199 cout << "<BEGIN" << endl;
201 if ( !updateTrainingSet( tempLeafFARate ) )
203 cout << "Train dataset for temp stage can not be filled. "
204 "Branch training terminated." << endl;
207 if( tempLeafFARate <= requiredLeafFARate )
209 cout << "Required leaf false alarm rate achieved. "
210 "Branch training terminated." << endl;
214 CvCascadeBoost* tempStage = new CvCascadeBoost;
215 bool isStageTrained = tempStage->train( (CvFeatureEvaluator*)featureEvaluator,
216 curNumSamples, _precalcValBufSize, _precalcIdxBufSize,
217 *((CvCascadeBoostParams*)stageParams) );
218 cout << "END>" << endl;
223 stageClassifiers.push_back( tempStage );
228 std::string paramsFilename = dirName + CC_PARAMS_FILENAME;
229 FileStorage fs( paramsFilename, FileStorage::WRITE);
230 if ( !fs.isOpened() )
232 cout << "Parameters can not be written, because file " << paramsFilename
233 << " can not be opened." << endl;
236 fs << FileStorage::getDefaultObjectName(paramsFilename) << "{";
240 // save current stage
242 sprintf(buf, "%s%d", "stage", i );
243 string stageFilename = dirName + buf + ".xml";
244 FileStorage fs( stageFilename, FileStorage::WRITE );
245 if ( !fs.isOpened() )
247 cout << "Current stage can not be written, because file " << stageFilename
248 << " can not be opened." << endl;
251 fs << FileStorage::getDefaultObjectName(stageFilename) << "{";
252 tempStage->write( fs, Mat() );
255 // Output training time up till now
256 float seconds = float( clock () - begin_time ) / CLOCKS_PER_SEC;
257 int days = int(seconds) / 60 / 60 / 24;
258 int hours = (int(seconds) / 60 / 60) % 24;
259 int minutes = (int(seconds) / 60) % 60;
260 int seconds_left = int(seconds) % 60;
261 cout << "Training until now has taken " << days << " days " << hours << " hours " << minutes << " minutes " << seconds_left <<" seconds." << endl;
264 if(stageClassifiers.size() == 0)
266 cout << "Cascade classifier can't be trained. Check the used training parameters." << endl;
270 save( dirName + CC_CASCADE_FILENAME, baseFormatSave );
275 int CvCascadeClassifier::predict( int sampleIdx )
277 CV_DbgAssert( sampleIdx < numPos + numNeg );
278 for (vector< Ptr<CvCascadeBoost> >::iterator it = stageClassifiers.begin();
279 it != stageClassifiers.end(); it++ )
281 if ( (*it)->predict( sampleIdx ) == 0.f )
287 bool CvCascadeClassifier::updateTrainingSet( double& acceptanceRatio)
289 int64 posConsumed = 0, negConsumed = 0;
291 int posCount = fillPassedSamples( 0, numPos, true, posConsumed );
294 cout << "POS count : consumed " << posCount << " : " << (int)posConsumed << endl;
296 int proNumNeg = cvRound( ( ((double)numNeg) * ((double)posCount) ) / numPos ); // apply only a fraction of negative samples. double is required since overflow is possible
297 int negCount = fillPassedSamples( posCount, proNumNeg, false, negConsumed );
301 curNumSamples = posCount + negCount;
302 acceptanceRatio = negConsumed == 0 ? 0 : ( (double)negCount/(double)(int64)negConsumed );
303 cout << "NEG count : acceptanceRatio " << negCount << " : " << acceptanceRatio << endl;
307 int CvCascadeClassifier::fillPassedSamples( int first, int count, bool isPositive, int64& consumed )
310 Mat img(cascadeParams.winSize, CV_8UC1);
311 for( int i = first; i < first + count; i++ )
315 bool isGetImg = isPositive ? imgReader.getPos( img ) :
316 imgReader.getNeg( img );
321 featureEvaluator->setImage( img, isPositive ? 1 : 0, i );
322 if( predict( i ) == 1.0F )
325 printf("%s current samples: %d\r", isPositive ? "POS":"NEG", getcount);
333 void CvCascadeClassifier::writeParams( FileStorage &fs ) const
335 cascadeParams.write( fs );
336 fs << CC_STAGE_PARAMS << "{"; stageParams->write( fs ); fs << "}";
337 fs << CC_FEATURE_PARAMS << "{"; featureParams->write( fs ); fs << "}";
340 void CvCascadeClassifier::writeFeatures( FileStorage &fs, const Mat& featureMap ) const
342 ((CvFeatureEvaluator*)((Ptr<CvFeatureEvaluator>)featureEvaluator))->writeFeatures( fs, featureMap );
345 void CvCascadeClassifier::writeStages( FileStorage &fs, const Mat& featureMap ) const
349 fs << CC_STAGES << "[";
350 for( vector< Ptr<CvCascadeBoost> >::const_iterator it = stageClassifiers.begin();
351 it != stageClassifiers.end(); it++, i++ )
353 sprintf( cmnt, "stage %d", i );
354 cvWriteComment( fs.fs, cmnt, 0 );
356 ((CvCascadeBoost*)((Ptr<CvCascadeBoost>)*it))->write( fs, featureMap );
362 bool CvCascadeClassifier::readParams( const FileNode &node )
364 if ( !node.isMap() || !cascadeParams.read( node ) )
367 stageParams = new CvCascadeBoostParams;
368 FileNode rnode = node[CC_STAGE_PARAMS];
369 if ( !stageParams->read( rnode ) )
372 featureParams = CvFeatureParams::create(cascadeParams.featureType);
373 rnode = node[CC_FEATURE_PARAMS];
374 if ( !featureParams->read( rnode ) )
379 bool CvCascadeClassifier::readStages( const FileNode &node)
381 FileNode rnode = node[CC_STAGES];
382 if (!rnode.empty() || !rnode.isSeq())
384 stageClassifiers.reserve(numStages);
385 FileNodeIterator it = rnode.begin();
386 for( int i = 0; i < min( (int)rnode.size(), numStages ); i++, it++ )
388 CvCascadeBoost* tempStage = new CvCascadeBoost;
389 if ( !tempStage->read( *it, (CvFeatureEvaluator *)featureEvaluator, *((CvCascadeBoostParams*)stageParams) ) )
394 stageClassifiers.push_back(tempStage);
399 // For old Haar Classifier file saving
400 #define ICV_HAAR_SIZE_NAME "size"
401 #define ICV_HAAR_STAGES_NAME "stages"
402 #define ICV_HAAR_TREES_NAME "trees"
403 #define ICV_HAAR_FEATURE_NAME "feature"
404 #define ICV_HAAR_RECTS_NAME "rects"
405 #define ICV_HAAR_TILTED_NAME "tilted"
406 #define ICV_HAAR_THRESHOLD_NAME "threshold"
407 #define ICV_HAAR_LEFT_NODE_NAME "left_node"
408 #define ICV_HAAR_LEFT_VAL_NAME "left_val"
409 #define ICV_HAAR_RIGHT_NODE_NAME "right_node"
410 #define ICV_HAAR_RIGHT_VAL_NAME "right_val"
411 #define ICV_HAAR_STAGE_THRESHOLD_NAME "stage_threshold"
412 #define ICV_HAAR_PARENT_NAME "parent"
413 #define ICV_HAAR_NEXT_NAME "next"
415 void CvCascadeClassifier::save( const string filename, bool baseFormat )
417 FileStorage fs( filename, FileStorage::WRITE );
419 if ( !fs.isOpened() )
422 fs << FileStorage::getDefaultObjectName(filename) << "{";
426 getUsedFeaturesIdxMap( featureMap );
428 fs << CC_STAGE_NUM << (int)stageClassifiers.size();
429 writeStages( fs, featureMap );
430 writeFeatures( fs, featureMap );
436 if ( cascadeParams.featureType != CvFeatureParams::HAAR )
437 CV_Error( CV_StsBadFunc, "old file format is used for Haar-like features only");
438 fs << ICV_HAAR_SIZE_NAME << "[:" << cascadeParams.winSize.width <<
439 cascadeParams.winSize.height << "]";
440 fs << ICV_HAAR_STAGES_NAME << "[";
441 for( size_t si = 0; si < stageClassifiers.size(); si++ )
444 /*sprintf( buf, "stage %d", si );
445 CV_CALL( cvWriteComment( fs, buf, 1 ) );*/
446 weak = stageClassifiers[si]->get_weak_predictors();
447 fs << ICV_HAAR_TREES_NAME << "[";
448 for( int wi = 0; wi < weak->total; wi++ )
450 int inner_node_idx = -1, total_inner_node_idx = -1;
451 queue<const CvDTreeNode*> inner_nodes_queue;
452 CvCascadeBoostTree* tree = *((CvCascadeBoostTree**) cvGetSeqElem( weak, wi ));
455 /*sprintf( buf, "tree %d", wi );
456 CV_CALL( cvWriteComment( fs, buf, 1 ) );*/
458 const CvDTreeNode* tempNode;
460 inner_nodes_queue.push( tree->get_root() );
461 total_inner_node_idx++;
463 while (!inner_nodes_queue.empty())
465 tempNode = inner_nodes_queue.front();
469 fs << ICV_HAAR_FEATURE_NAME << "{";
470 ((CvHaarEvaluator*)((CvFeatureEvaluator*)featureEvaluator))->writeFeature( fs, tempNode->split->var_idx );
473 fs << ICV_HAAR_THRESHOLD_NAME << tempNode->split->ord.c;
475 if( tempNode->left->left || tempNode->left->right )
477 inner_nodes_queue.push( tempNode->left );
478 total_inner_node_idx++;
479 fs << ICV_HAAR_LEFT_NODE_NAME << total_inner_node_idx;
482 fs << ICV_HAAR_LEFT_VAL_NAME << tempNode->left->value;
484 if( tempNode->right->left || tempNode->right->right )
486 inner_nodes_queue.push( tempNode->right );
487 total_inner_node_idx++;
488 fs << ICV_HAAR_RIGHT_NODE_NAME << total_inner_node_idx;
491 fs << ICV_HAAR_RIGHT_VAL_NAME << tempNode->right->value;
492 fs << "}"; // ICV_HAAR_FEATURE_NAME
493 inner_nodes_queue.pop();
497 fs << "]"; //ICV_HAAR_TREES_NAME
498 fs << ICV_HAAR_STAGE_THRESHOLD_NAME << stageClassifiers[si]->getThreshold();
499 fs << ICV_HAAR_PARENT_NAME << (int)si-1 << ICV_HAAR_NEXT_NAME << -1;
501 } /* for each stage */
502 fs << "]"; //ICV_HAAR_STAGES_NAME
507 bool CvCascadeClassifier::load( const string cascadeDirName )
509 FileStorage fs( cascadeDirName + CC_PARAMS_FILENAME, FileStorage::READ );
510 if ( !fs.isOpened() )
512 FileNode node = fs.getFirstTopLevelNode();
513 if ( !readParams( node ) )
515 featureEvaluator = CvFeatureEvaluator::create(cascadeParams.featureType);
516 featureEvaluator->init( ((CvFeatureParams*)featureParams), numPos + numNeg, cascadeParams.winSize );
520 for ( int si = 0; si < numStages; si++ )
522 sprintf( buf, "%s%d", "stage", si);
523 fs.open( cascadeDirName + buf + ".xml", FileStorage::READ );
524 node = fs.getFirstTopLevelNode();
525 if ( !fs.isOpened() )
527 CvCascadeBoost *tempStage = new CvCascadeBoost;
529 if ( !tempStage->read( node, (CvFeatureEvaluator*)featureEvaluator, *((CvCascadeBoostParams*)stageParams )) )
535 stageClassifiers.push_back(tempStage);
540 void CvCascadeClassifier::getUsedFeaturesIdxMap( Mat& featureMap )
542 int varCount = featureEvaluator->getNumFeatures() * featureEvaluator->getFeatureSize();
543 featureMap.create( 1, varCount, CV_32SC1 );
544 featureMap.setTo(Scalar(-1));
546 for( vector< Ptr<CvCascadeBoost> >::const_iterator it = stageClassifiers.begin();
547 it != stageClassifiers.end(); it++ )
548 ((CvCascadeBoost*)((Ptr<CvCascadeBoost>)(*it)))->markUsedFeaturesInMap( featureMap );
550 for( int fi = 0, idx = 0; fi < varCount; fi++ )
551 if ( featureMap.at<int>(0, fi) >= 0 )
552 featureMap.ptr<int>(0)[fi] = idx++;