mirror of
https://github.com/opencv/opencv.git
synced 2026-07-21 19:33:03 +04:00
d86d8ed909
PR #2968:cce2d998578f9cFixed bug which caused crash of GPU version of feature matcher in stitcher The bug caused crash of GPU version of feature matcher in stitcher when we use ORB features. PR #3236:5947519Check sure that we're not already below required leaf false alarm rate before continuing to get negative samples. PR #3190 fix blobdetector PR #3562 (part):82bd82eTBB updated to 4.3u2. Fix for aarch64 support. PR #3604 (part):091c7a3OpenGL interop sample reworked not ot use cvconfig.h PR #3792:afdf319Add -L for CUDA libs path to pkg-config Add all dirs from CUDA_LIBS_PATH as -L linker options to OPENCV_LINKER_LIBS. These will end up in opencv.pc. PR #3893:122b9f8Turn ocv_convert_to_lib_name into a function PR #5490:ec5244afixed memory leak in findHomography tests PR #5491:0d5b739delete video readers PR #5574 PR #5202
566 lines
21 KiB
C++
566 lines
21 KiB
C++
#include "opencv2/core.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 = makePtr<CvCascadeBoostParams>();
|
|
*stageParams = _stageParams;
|
|
featureEvaluator = CvFeatureEvaluator::create(cascadeParams.featureType);
|
|
featureEvaluator->init( 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;
|
|
}
|
|
|
|
Ptr<CvCascadeBoost> tempStage = makePtr<CvCascadeBoost>();
|
|
bool isStageTrained = tempStage->train( featureEvaluator,
|
|
curNumSamples, _precalcValBufSize, _precalcIdxBufSize,
|
|
*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
|
|
{
|
|
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 << "{";
|
|
(*it)->write( fs, featureMap );
|
|
fs << "}";
|
|
}
|
|
fs << "]";
|
|
}
|
|
|
|
bool CvCascadeClassifier::readParams( const FileNode &node )
|
|
{
|
|
if ( !node.isMap() || !cascadeParams.read( node ) )
|
|
return false;
|
|
|
|
stageParams = makePtr<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++ )
|
|
{
|
|
Ptr<CvCascadeBoost> tempStage = makePtr<CvCascadeBoost>();
|
|
if ( !tempStage->read( *it, featureEvaluator, *stageParams) )
|
|
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*)featureEvaluator.get())->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( 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;
|
|
Ptr<CvCascadeBoost> tempStage = makePtr<CvCascadeBoost>();
|
|
|
|
if ( !tempStage->read( node, featureEvaluator, *stageParams ))
|
|
{
|
|
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++ )
|
|
(*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++;
|
|
}
|