Commit 6910358f3022ba7f5996f4142d9a158a7f1cf85d

Authored by Jordan Cheney
1 parent d9fe1bd0

Step towards step2

openbr/core/_cascade.cpp 0 → 100644
  1 +#include "_cascade.h"
  2 +#include <stdio.h>
  3 +#include <iostream>
  4 +#include <fstream>
  5 +
  6 +using namespace std;
  7 +using namespace br;
  8 +using namespace cv;
  9 +
  10 +bool CascadeImageReader::create( const vector<Mat> &_posImages, const vector<Mat> &_negImages, Size _winSize )
  11 +{
  12 + posImages = _posImages;
  13 + negImages = _negImages;
  14 + winSize = _winSize;
  15 + return posReader.create(_posImages) && negReader.create(_negImages, _winSize);
  16 +}
  17 +
  18 +CascadeImageReader::NegReader::NegReader()
  19 +{
  20 + src.create( 0, 0 , CV_8UC1 );
  21 + img.create( 0, 0, CV_8UC1 );
  22 + point = offset = Point( 0, 0 );
  23 + scale = 1.0F;
  24 + scaleFactor = 1.4142135623730950488016887242097F;
  25 + stepFactor = 0.5F;
  26 +}
  27 +
  28 +bool CascadeImageReader::NegReader::create( const vector<Mat> &_negImages, Size _winSize )
  29 +{
  30 + negImages = _negImages;
  31 + winSize = _winSize;
  32 + last = round = 0;
  33 + return true;
  34 +}
  35 +
  36 +bool CascadeImageReader::NegReader::nextImg()
  37 +{
  38 + Point _offset = Point(0,0);
  39 + size_t count = negImages.size();
  40 + for( size_t i = 0; i < count; i++ )
  41 + {
  42 + src = negImages[last++];
  43 + if( src.empty() )
  44 + continue;
  45 + round += last / count;
  46 + round = round % (winSize.width * winSize.height);
  47 + last %= count;
  48 +
  49 + _offset.x = std::min( (int)round % winSize.width, src.cols - winSize.width );
  50 + _offset.y = std::min( (int)round / winSize.width, src.rows - winSize.height );
  51 + if( !src.empty() && src.type() == CV_8UC1
  52 + && _offset.x >= 0 && _offset.y >= 0 )
  53 + break;
  54 + }
  55 +
  56 + if( src.empty() )
  57 + return false; // no appropriate image
  58 + point = offset = _offset;
  59 + scale = max( ((float)winSize.width + point.x) / ((float)src.cols),
  60 + ((float)winSize.height + point.y) / ((float)src.rows) );
  61 +
  62 + Size sz( (int)(scale*src.cols + 0.5F), (int)(scale*src.rows + 0.5F) );
  63 + resize( src, img, sz );
  64 + return true;
  65 +}
  66 +
  67 +bool CascadeImageReader::NegReader::get( Mat& _img )
  68 +{
  69 + CV_Assert( !_img.empty() );
  70 + CV_Assert( _img.type() == CV_8UC1 );
  71 + CV_Assert( _img.cols == winSize.width );
  72 + CV_Assert( _img.rows == winSize.height );
  73 +
  74 + if( img.empty() )
  75 + if ( !nextImg() )
  76 + return false;
  77 +
  78 + Mat mat( winSize.height, winSize.width, CV_8UC1,
  79 + (void*)(img.data + point.y * img.step + point.x * img.elemSize()), img.step );
  80 + mat.copyTo(_img);
  81 +
  82 + if( (int)( point.x + (1.0F + stepFactor ) * winSize.width ) < img.cols )
  83 + point.x += (int)(stepFactor * winSize.width);
  84 + else
  85 + {
  86 + point.x = offset.x;
  87 + if( (int)( point.y + (1.0F + stepFactor ) * winSize.height ) < img.rows )
  88 + point.y += (int)(stepFactor * winSize.height);
  89 + else
  90 + {
  91 + point.y = offset.y;
  92 + scale *= scaleFactor;
  93 + if( scale <= 1.0F )
  94 + resize( src, img, Size( (int)(scale*src.cols), (int)(scale*src.rows) ) );
  95 + else
  96 + {
  97 + if ( !nextImg() )
  98 + return false;
  99 + }
  100 + }
  101 + }
  102 + return true;
  103 +}
  104 +
  105 +CascadeImageReader::PosReader::PosReader()
  106 +{
  107 + file = 0;
  108 + vec = 0;
  109 + last = 0;
  110 +}
  111 +
  112 +bool CascadeImageReader::PosReader::create( const vector<Mat> &_posImages )
  113 +{
  114 + posImages = _posImages;
  115 + return true;
  116 +}
  117 +
  118 +bool CascadeImageReader::getPos(Mat &_img)
  119 +{
  120 + if (last > (int)posImages.size())
  121 + CV_Error( CV_StsBadArg, "Can not get new positive sample. vec-file is over.\n");
  122 + _img = posImages[posIdx++];
  123 + return true;
  124 +}
  125 +
  126 +//---------------------------- CascadeParams --------------------------------------
  127 +
  128 +static const char* stageTypes[] = { CC_BOOST };
  129 +static const char* featureTypes[] = { CC_LBP, CC_HAAR, CC_HOG, CC_HOGMULTI, CC_NPD };
  130 +
  131 +CascadeParams::CascadeParams() : stageType( defaultStageType ),
  132 + featureType( defaultFeatureType ), winSize( cvSize(24, 24) )
  133 +{
  134 + name = CC_CASCADE_PARAMS;
  135 +}
  136 +CascadeParams::CascadeParams( int _stageType, int _featureType ) : stageType( _stageType ),
  137 + featureType( _featureType ), winSize( cvSize(24, 24) )
  138 +{
  139 + name = CC_CASCADE_PARAMS;
  140 +}
  141 +
  142 +void CascadeParams::write( FileStorage &fs ) const
  143 +{
  144 + string stageTypeStr = stageType == BOOST ? CC_BOOST : string();
  145 + CV_Assert( !stageTypeStr.empty() );
  146 + fs << CC_STAGE_TYPE << stageTypeStr;
  147 + string featureTypeStr = featureType == FeatureParams::LBP ? CC_LBP :
  148 + 0;
  149 + CV_Assert( !stageTypeStr.empty() );
  150 + fs << CC_FEATURE_TYPE << featureTypeStr;
  151 + fs << CC_HEIGHT << winSize.height;
  152 + fs << CC_WIDTH << winSize.width;
  153 +}
  154 +
  155 +bool CascadeParams::read( const FileNode &node )
  156 +{
  157 + if ( node.empty() )
  158 + return false;
  159 + string stageTypeStr, featureTypeStr;
  160 + FileNode rnode = node[CC_STAGE_TYPE];
  161 + if ( !rnode.isString() )
  162 + return false;
  163 + rnode >> stageTypeStr;
  164 + stageType = !stageTypeStr.compare( CC_BOOST ) ? BOOST : -1;
  165 + if (stageType == -1)
  166 + return false;
  167 + rnode = node[CC_FEATURE_TYPE];
  168 + if ( !rnode.isString() )
  169 + return false;
  170 + rnode >> featureTypeStr;
  171 + featureType = !featureTypeStr.compare( CC_LBP ) ? FeatureParams::LBP :
  172 + -1;
  173 + if (featureType == -1)
  174 + return false;
  175 + node[CC_HEIGHT] >> winSize.height;
  176 + node[CC_WIDTH] >> winSize.width;
  177 + return winSize.height > 0 && winSize.width > 0;
  178 +}
  179 +
  180 +void CascadeParams::printDefaults() const
  181 +{
  182 + Params::printDefaults();
  183 + cout << " [-stageType <";
  184 + for( int i = 0; i < (int)(sizeof(stageTypes)/sizeof(stageTypes[0])); i++ )
  185 + {
  186 + cout << (i ? " | " : "") << stageTypes[i];
  187 + if ( i == defaultStageType )
  188 + cout << "(default)";
  189 + }
  190 + cout << ">]" << endl;
  191 +
  192 + cout << " [-featureType <{";
  193 + for( int i = 0; i < (int)(sizeof(featureTypes)/sizeof(featureTypes[0])); i++ )
  194 + {
  195 + cout << (i ? ", " : "") << featureTypes[i];
  196 + if ( i == defaultStageType )
  197 + cout << "(default)";
  198 + }
  199 + cout << "}>]" << endl;
  200 + cout << " [-w <sampleWidth = " << winSize.width << ">]" << endl;
  201 + cout << " [-h <sampleHeight = " << winSize.height << ">]" << endl;
  202 +}
  203 +
  204 +void CascadeParams::printAttrs() const
  205 +{
  206 + cout << "stageType: " << stageTypes[stageType] << endl;
  207 + cout << "featureType: " << featureTypes[featureType] << endl;
  208 + cout << "sampleWidth: " << winSize.width << endl;
  209 + cout << "sampleHeight: " << winSize.height << endl;
  210 +}
  211 +
  212 +bool CascadeParams::scanAttr( const string prmName, const string val )
  213 +{
  214 + bool res = true;
  215 + if( !prmName.compare( "-stageType" ) )
  216 + {
  217 + for( int i = 0; i < (int)(sizeof(stageTypes)/sizeof(stageTypes[0])); i++ )
  218 + if( !val.compare( stageTypes[i] ) )
  219 + stageType = i;
  220 + }
  221 + else if( !prmName.compare( "-featureType" ) )
  222 + {
  223 + for( int i = 0; i < (int)(sizeof(featureTypes)/sizeof(featureTypes[0])); i++ )
  224 + if( !val.compare( featureTypes[i] ) )
  225 + featureType = i;
  226 + }
  227 + else if( !prmName.compare( "-w" ) )
  228 + {
  229 + winSize.width = atoi( val.c_str() );
  230 + }
  231 + else if( !prmName.compare( "-h" ) )
  232 + {
  233 + winSize.height = atoi( val.c_str() );
  234 + }
  235 + else
  236 + res = false;
  237 + return res;
  238 +}
  239 +
  240 +//---------------------------- CascadeClassifier --------------------------------------
  241 +
  242 +bool BrCascadeClassifier::train(const string _cascadeDirName,
  243 + const vector<Mat> &_posImages,
  244 + const vector<Mat> &_negImages,
  245 + int _numPos, int _numNeg,
  246 + int _precalcValBufSize, int _precalcIdxBufSize,
  247 + int _numStages,
  248 + const CascadeParams& _cascadeParams,
  249 + const FeatureParams& _featureParams,
  250 + const CascadeBoostParams& _stageParams,
  251 + bool baseFormatSave )
  252 +{
  253 + // Start recording clock ticks for training time output
  254 + const clock_t begin_time = clock();
  255 +
  256 + if( _cascadeDirName.empty() )
  257 + CV_Error( CV_StsBadArg, "_cascadeDirName is NULL" );
  258 +
  259 + string dirName;
  260 + if (_cascadeDirName.find_last_of("/\\") == (_cascadeDirName.length() - 1) )
  261 + dirName = _cascadeDirName;
  262 + else
  263 + dirName = _cascadeDirName + '/';
  264 +
  265 + numPos = _numPos;
  266 + numNeg = _numNeg;
  267 + numStages = _numStages;
  268 + imgReader.create( _posImages, _negImages, _cascadeParams.winSize );
  269 +
  270 + if ( !load( dirName ) )
  271 + {
  272 + cascadeParams = _cascadeParams;
  273 + featureParams = FeatureParams::create(cascadeParams.featureType);
  274 + featureParams->init(_featureParams);
  275 + stageParams = new CascadeBoostParams;
  276 + *stageParams = _stageParams;
  277 + featureEvaluator = FeatureEvaluator::create(cascadeParams.featureType);
  278 + featureEvaluator->init( (FeatureParams*)featureParams, numPos + numNeg, cascadeParams.winSize );
  279 + stageClassifiers.reserve( numStages );
  280 + }
  281 + cout << "PARAMETERS:" << endl;
  282 + cout << "cascadeDirName: " << _cascadeDirName << endl;
  283 + cout << "numPos: " << _numPos << endl;
  284 + cout << "numNeg: " << _numNeg << endl;
  285 + cout << "numStages: " << numStages << endl;
  286 + cout << "precalcValBufSize[Mb] : " << _precalcValBufSize << endl;
  287 + cout << "precalcIdxBufSize[Mb] : " << _precalcIdxBufSize << endl;
  288 + cascadeParams.printAttrs();
  289 + stageParams->printAttrs();
  290 + featureParams->printAttrs();
  291 +
  292 + int startNumStages = (int)stageClassifiers.size();
  293 + if ( startNumStages > 1 )
  294 + cout << endl << "Stages 0-" << startNumStages-1 << " are loaded" << endl;
  295 + else if ( startNumStages == 1)
  296 + cout << endl << "Stage 0 is loaded" << endl;
  297 +
  298 + double requiredLeafFARate = pow( (double) stageParams->maxFalseAlarm, (double) numStages ) /
  299 + (double)stageParams->max_depth;
  300 + double tempLeafFARate;
  301 +
  302 + for( int i = startNumStages; i < numStages; i++ )
  303 + {
  304 + cout << endl << "===== TRAINING " << i << "-stage =====" << endl;
  305 + cout << "<BEGIN" << endl;
  306 + if ( !updateTrainingSet( tempLeafFARate ) )
  307 + {
  308 + cout << "Train dataset for temp stage can not be filled. "
  309 + "Branch training terminated." << endl;
  310 + break;
  311 + }
  312 + if( tempLeafFARate <= requiredLeafFARate )
  313 + {
  314 + cout << "Required leaf false alarm rate achieved. "
  315 + "Branch training terminated." << endl;
  316 + break;
  317 + }
  318 +
  319 + CascadeBoost* tempStage = new CascadeBoost;
  320 + bool isStageTrained = tempStage->train( (FeatureEvaluator*)featureEvaluator,
  321 + curNumSamples, _precalcValBufSize, _precalcIdxBufSize,
  322 + *((CascadeBoostParams*)stageParams) );
  323 + cout << "END>" << endl;
  324 +
  325 + if(!isStageTrained)
  326 + break;
  327 +
  328 + stageClassifiers.push_back( tempStage );
  329 +
  330 + // save params
  331 + if( i == 0)
  332 + {
  333 + std::string paramsFilename = dirName + CC_PARAMS_FILENAME;
  334 + FileStorage fs( paramsFilename, FileStorage::WRITE);
  335 + if ( !fs.isOpened() )
  336 + {
  337 + cout << "Parameters can not be written, because file " << paramsFilename
  338 + << " can not be opened." << endl;
  339 + return false;
  340 + }
  341 + fs << FileStorage::getDefaultObjectName(paramsFilename) << "{";
  342 + writeParams( fs );
  343 + fs << "}";
  344 + }
  345 + // save current stage
  346 + char buf[10];
  347 + sprintf(buf, "%s%d", "stage", i );
  348 + string stageFilename = dirName + buf + ".xml";
  349 + FileStorage fs( stageFilename, FileStorage::WRITE );
  350 + if ( !fs.isOpened() )
  351 + {
  352 + cout << "Current stage can not be written, because file " << stageFilename
  353 + << " can not be opened." << endl;
  354 + return false;
  355 + }
  356 + fs << FileStorage::getDefaultObjectName(stageFilename) << "{";
  357 + tempStage->write( fs, Mat() );
  358 + fs << "}";
  359 +
  360 + // Output training time up till now
  361 + float seconds = float( clock () - begin_time ) / CLOCKS_PER_SEC;
  362 + int days = int(seconds) / 60 / 60 / 24;
  363 + int hours = (int(seconds) / 60 / 60) % 24;
  364 + int minutes = (int(seconds) / 60) % 60;
  365 + int seconds_left = int(seconds) % 60;
  366 + cout << "Training until now has taken " << days << " days " << hours << " hours " << minutes << " minutes " << seconds_left <<" seconds." << endl;
  367 + }
  368 +
  369 + if(stageClassifiers.size() == 0)
  370 + {
  371 + cout << "Cascade classifier can't be trained. Check the used training parameters." << endl;
  372 + return false;
  373 + }
  374 +
  375 + save( dirName + CC_CASCADE_FILENAME, baseFormatSave );
  376 +
  377 + return true;
  378 +}
  379 +
  380 +int BrCascadeClassifier::predict( int sampleIdx )
  381 +{
  382 + CV_DbgAssert( sampleIdx < numPos + numNeg );
  383 + for (vector< Ptr<CascadeBoost> >::iterator it = stageClassifiers.begin();
  384 + it != stageClassifiers.end(); it++ )
  385 + {
  386 + if ( (*it)->predict( sampleIdx ) == 0.f )
  387 + return 0;
  388 + }
  389 + return 1;
  390 +}
  391 +
  392 +bool BrCascadeClassifier::updateTrainingSet( double& acceptanceRatio)
  393 +{
  394 + int64 posConsumed = 0, negConsumed = 0;
  395 + imgReader.restart();
  396 +
  397 + int posCount = fillPassedSamples( 0, numPos, true, posConsumed );
  398 + if( !posCount )
  399 + return false;
  400 + cout << "POS count : consumed " << posCount << " : " << (int)posConsumed << endl;
  401 +
  402 + int proNumNeg = cvRound( ( ((double)numNeg) * ((double)posCount) ) / numPos ); // apply only a fraction of negative samples. double is required since overflow is possible
  403 + int negCount = fillPassedSamples( posCount, proNumNeg, false, negConsumed );
  404 + if ( !negCount )
  405 + return false;
  406 +
  407 + curNumSamples = posCount + negCount;
  408 + acceptanceRatio = negConsumed == 0 ? 0 : ( (double)negCount/(double)(int64)negConsumed );
  409 + cout << "NEG count : acceptanceRatio " << negCount << " : " << acceptanceRatio << endl;
  410 + return true;
  411 +}
  412 +
  413 +int BrCascadeClassifier::fillPassedSamples( int first, int count, bool isPositive, int64& consumed )
  414 +{
  415 + int getcount = 0;
  416 + Mat img(cascadeParams.winSize, CV_8UC1);
  417 + for( int i = first; i < first + count; i++ )
  418 + {
  419 + for( ; ; )
  420 + {
  421 + bool isGetImg = isPositive ? imgReader.getPos( img ) :
  422 + imgReader.getNeg( img );
  423 + if( !isGetImg )
  424 + return getcount;
  425 + consumed++;
  426 +
  427 + featureEvaluator->setImage( img, isPositive ? 1 : 0, i );
  428 + if( predict( i ) == 1.0F )
  429 + {
  430 + getcount++;
  431 + printf("%s current samples: %d\r", isPositive ? "POS":"NEG", getcount);
  432 + break;
  433 + }
  434 + }
  435 + }
  436 + return getcount;
  437 +}
  438 +
  439 +void BrCascadeClassifier::writeParams( FileStorage &fs ) const
  440 +{
  441 + cascadeParams.write( fs );
  442 + fs << CC_STAGE_PARAMS << "{"; stageParams->write( fs ); fs << "}";
  443 + fs << CC_FEATURE_PARAMS << "{"; featureParams->write( fs ); fs << "}";
  444 +}
  445 +
  446 +void BrCascadeClassifier::writeFeatures( FileStorage &fs, const Mat& featureMap ) const
  447 +{
  448 + ((FeatureEvaluator*)((Ptr<FeatureEvaluator>)featureEvaluator))->writeFeatures( fs, featureMap );
  449 +}
  450 +
  451 +void BrCascadeClassifier::writeStages( FileStorage &fs, const Mat& featureMap ) const
  452 +{
  453 + char cmnt[30];
  454 + int i = 0;
  455 + fs << CC_STAGES << "[";
  456 + for( vector< Ptr<CascadeBoost> >::const_iterator it = stageClassifiers.begin();
  457 + it != stageClassifiers.end(); it++, i++ )
  458 + {
  459 + sprintf( cmnt, "stage %d", i );
  460 + cvWriteComment( fs.fs, cmnt, 0 );
  461 + fs << "{";
  462 + ((CascadeBoost*)((Ptr<CascadeBoost>)*it))->write( fs, featureMap );
  463 + fs << "}";
  464 + }
  465 + fs << "]";
  466 +}
  467 +
  468 +bool BrCascadeClassifier::readParams( const FileNode &node )
  469 +{
  470 + if ( !node.isMap() || !cascadeParams.read( node ) )
  471 + return false;
  472 + stageParams = new CascadeBoostParams;
  473 + FileNode rnode = node[CC_STAGE_PARAMS];
  474 + if ( !stageParams->read( rnode ) )
  475 + return false;
  476 + featureParams = FeatureParams::create(cascadeParams.featureType);
  477 + rnode = node[CC_FEATURE_PARAMS];
  478 + if ( !featureParams->read( rnode ) )
  479 + return false;
  480 + return true;
  481 +}
  482 +
  483 +bool BrCascadeClassifier::readStages( const FileNode &node)
  484 +{
  485 + FileNode rnode = node[CC_STAGES];
  486 + if (!rnode.empty() || !rnode.isSeq())
  487 + return false;
  488 + stageClassifiers.reserve(numStages);
  489 + FileNodeIterator it = rnode.begin();
  490 + for( int i = 0; i < min( (int)rnode.size(), numStages ); i++, it++ )
  491 + {
  492 + CascadeBoost* tempStage = new CascadeBoost;
  493 + if ( !tempStage->read( *it, (FeatureEvaluator *)featureEvaluator, *((CascadeBoostParams*)stageParams) ) )
  494 + {
  495 + delete tempStage;
  496 + return false;
  497 + }
  498 + stageClassifiers.push_back(tempStage);
  499 + }
  500 + return true;
  501 +}
  502 +
  503 +void BrCascadeClassifier::save( const string filename, bool baseFormat )
  504 +{
  505 + FileStorage fs( filename, FileStorage::WRITE );
  506 +
  507 + if ( !fs.isOpened() )
  508 + return;
  509 +
  510 + fs << FileStorage::getDefaultObjectName(filename) << "{";
  511 + if ( !baseFormat )
  512 + {
  513 + Mat featureMap;
  514 + getUsedFeaturesIdxMap( featureMap );
  515 + writeParams( fs );
  516 + fs << CC_STAGE_NUM << (int)stageClassifiers.size();
  517 + writeStages( fs, featureMap );
  518 + writeFeatures( fs, featureMap );
  519 + }
  520 + else
  521 + {
  522 + qFatal("Old style cascade. Not sure how you got here but it's not supported");
  523 + }
  524 + fs << "}";
  525 +}
  526 +
  527 +bool BrCascadeClassifier::load( const string cascadeDirName )
  528 +{
  529 + FileStorage fs( cascadeDirName + CC_PARAMS_FILENAME, FileStorage::READ );
  530 + if ( !fs.isOpened() )
  531 + return false;
  532 +
  533 + FileNode node = fs.getFirstTopLevelNode();
  534 + if ( !readParams( node ) )
  535 + return false;
  536 +
  537 + featureEvaluator = FeatureEvaluator::create(cascadeParams.featureType);
  538 + featureEvaluator->init( ((FeatureParams*)featureParams), numPos + numNeg, cascadeParams.winSize );
  539 + fs.release();
  540 +
  541 + char buf[10];
  542 + for ( int si = 0; si < numStages; si++ )
  543 + {
  544 + sprintf( buf, "%s%d", "stage", si);
  545 + fs.open( cascadeDirName + buf + ".xml", FileStorage::READ );
  546 + node = fs.getFirstTopLevelNode();
  547 + if ( !fs.isOpened() )
  548 + break;
  549 + CascadeBoost *tempStage = new CascadeBoost;
  550 +
  551 + if ( !tempStage->read( node, (FeatureEvaluator*)featureEvaluator, *((CascadeBoostParams*)stageParams )) )
  552 + {
  553 + delete tempStage;
  554 + fs.release();
  555 + break;
  556 + }
  557 + stageClassifiers.push_back(tempStage);
  558 + }
  559 + return true;
  560 +}
  561 +
  562 +void BrCascadeClassifier::getUsedFeaturesIdxMap( Mat& featureMap )
  563 +{
  564 + int varCount = featureEvaluator->getNumFeatures() * featureEvaluator->getFeatureSize();
  565 + featureMap.create( 1, varCount, CV_32SC1 );
  566 + featureMap.setTo(Scalar(-1));
  567 +
  568 + for( vector< Ptr<CascadeBoost> >::const_iterator it = stageClassifiers.begin();
  569 + it != stageClassifiers.end(); it++ )
  570 + ((CascadeBoost*)((Ptr<CascadeBoost>)(*it)))->markUsedFeaturesInMap( featureMap );
  571 +
  572 + for( int fi = 0, idx = 0; fi < varCount; fi++ )
  573 + if ( featureMap.at<int>(0, fi) >= 0 )
  574 + featureMap.ptr<int>(0)[fi] = idx++;
  575 +}
  576 +
  577 +
openbr/core/_cascade.h 0 → 100644
  1 +#ifndef _CASCADE_H
  2 +#define _CASCADE_H
  3 +
  4 +#include <openbr/openbr_plugin.h>
  5 +#include <opencv2/highgui/highgui.hpp>
  6 +#include "features.h"
  7 +#include "boost.h"
  8 +
  9 +namespace br
  10 +{
  11 +
  12 +class CascadeImageReader
  13 +{
  14 +public:
  15 + bool create( const std::vector<cv::Mat> &_posImages, const std::vector<cv::Mat> &_negImages, cv::Size _winSize );
  16 + void restart() { posIdx = 0; }
  17 + bool getNeg(cv::Mat &_img);
  18 + bool getPos(cv::Mat &_img);
  19 +
  20 +private:
  21 + std::vector<cv::Mat> posImages, negImages;
  22 +
  23 + int posIdx, negIdx;
  24 +
  25 + cv::Mat src, img;
  26 + cv::Point offset, point;
  27 + float scale;
  28 + float scaleFactor;
  29 + float stepFactor;
  30 + size_t round;
  31 + cv::Size winSize;
  32 +};
  33 +
  34 +class CascadeParams : public Params
  35 +{
  36 +public:
  37 + enum { BOOST = 0 };
  38 + static const int defaultStageType = BOOST;
  39 + static const int defaultFeatureType = FeatureParams::LBP;
  40 +
  41 + CascadeParams();
  42 + CascadeParams( int _stageType, int _featureType );
  43 + void write( cv::FileStorage &fs ) const;
  44 + bool read( const cv::FileNode &node );
  45 +
  46 + void printDefaults() const;
  47 + void printAttrs() const;
  48 + bool scanAttr( const std::string prmName, const std::string val );
  49 +
  50 + int stageType;
  51 + int featureType;
  52 + cv::Size winSize;
  53 +};
  54 +
  55 +class BrCascadeClassifier
  56 +{
  57 +public:
  58 + bool train( const std::string _cascadeDirName,
  59 + const std::vector<cv::Mat> &_posImages,
  60 + const std::vector<cv::Mat> &_negImages,
  61 + int _numPos, int _numNeg,
  62 + int _precalcValBufSize, int _precalcIdxBufSize,
  63 + int _numStages,
  64 + const CascadeParams& _cascadeParams,
  65 + const FeatureParams& _featureParams,
  66 + const CascadeBoostParams& _stageParams,
  67 + bool baseFormatSave = false );
  68 +private:
  69 + int predict( int sampleIdx );
  70 + void save( const std::string cascadeDirName, bool baseFormat = false );
  71 + bool load( const std::string cascadeDirName );
  72 + bool updateTrainingSet( double& acceptanceRatio );
  73 + int fillPassedSamples( int first, int count, bool isPositive, int64& consumed );
  74 +
  75 + void writeParams( cv::FileStorage &fs ) const;
  76 + void writeStages( cv::FileStorage &fs, const cv::Mat& featureMap ) const;
  77 + void writeFeatures( cv::FileStorage &fs, const cv::Mat& featureMap ) const;
  78 + bool readParams( const cv::FileNode &node );
  79 + bool readStages( const cv::FileNode &node );
  80 +
  81 + void getUsedFeaturesIdxMap( cv::Mat& featureMap );
  82 +
  83 + CascadeParams cascadeParams;
  84 + cv::Ptr<FeatureParams> featureParams;
  85 + cv::Ptr<CascadeBoostParams> stageParams;
  86 +
  87 + cv::Ptr<FeatureEvaluator> featureEvaluator;
  88 + std::vector< cv::Ptr<CascadeBoost> > stageClassifiers;
  89 + CascadeImageReader imgReader;
  90 + int numStages, curNumSamples;
  91 + int numPos, numNeg;
  92 +};
  93 +
  94 +} // namespace br
  95 +
  96 +#endif // _CASCADE_H
  97 +
openbr/core/cascade.cpp
@@ -6,7 +6,7 @@ @@ -6,7 +6,7 @@
6 using namespace std; 6 using namespace std;
7 using namespace br; 7 using namespace br;
8 using namespace cv; 8 using namespace cv;
9 - 9 +/*
10 bool CascadeImageReader::create( const string _posFilename, const string _negFilename, Size _winSize ) 10 bool CascadeImageReader::create( const string _posFilename, const string _negFilename, Size _winSize )
11 { 11 {
12 return posReader.create(_posFilename) && negReader.create(_negFilename, _winSize); 12 return posReader.create(_posFilename) && negReader.create(_negFilename, _winSize);
@@ -41,7 +41,7 @@ bool CascadeImageReader::NegReader::create( const string _filename, Size _winSiz @@ -41,7 +41,7 @@ bool CascadeImageReader::NegReader::create( const string _filename, Size _winSiz
41 { 41 {
42 std::getline(file, str); 42 std::getline(file, str);
43 if (str.empty()) break; 43 if (str.empty()) break;
44 - if (str.at(0) == '#' ) continue; /* comment */ 44 + if (str.at(0) == '#' ) continue;
45 imgFilenames.push_back(dirname + str); 45 imgFilenames.push_back(dirname + str);
46 } 46 }
47 file.close(); 47 file.close();
@@ -186,7 +186,7 @@ CascadeImageReader::PosReader::~PosReader() @@ -186,7 +186,7 @@ CascadeImageReader::PosReader::~PosReader()
186 cvFree( &vec ); 186 cvFree( &vec );
187 } 187 }
188 188
189 -// -------------------------------------- Cascade -------------------------------------------- 189 +//---------------------------- CascadeParams --------------------------------------
190 190
191 static const char* stageTypes[] = { CC_BOOST }; 191 static const char* stageTypes[] = { CC_BOOST };
192 static const char* featureTypes[] = { CC_LBP, CC_HAAR, CC_HOG, CC_HOGMULTI, CC_NPD }; 192 static const char* featureTypes[] = { CC_LBP, CC_HAAR, CC_HOG, CC_HOGMULTI, CC_NPD };
@@ -202,8 +202,6 @@ CascadeParams::CascadeParams( int _stageType, int _featureType ) : stageType( _s @@ -202,8 +202,6 @@ CascadeParams::CascadeParams( int _stageType, int _featureType ) : stageType( _s
202 name = CC_CASCADE_PARAMS; 202 name = CC_CASCADE_PARAMS;
203 } 203 }
204 204
205 -//---------------------------- CascadeParams --------------------------------------  
206 -  
207 void CascadeParams::write( FileStorage &fs ) const 205 void CascadeParams::write( FileStorage &fs ) const
208 { 206 {
209 string stageTypeStr = stageType == BOOST ? CC_BOOST : string(); 207 string stageTypeStr = stageType == BOOST ? CC_BOOST : string();
@@ -304,7 +302,7 @@ bool CascadeParams::scanAttr( const string prmName, const string val ) @@ -304,7 +302,7 @@ bool CascadeParams::scanAttr( const string prmName, const string val )
304 302
305 //---------------------------- CascadeClassifier -------------------------------------- 303 //---------------------------- CascadeClassifier --------------------------------------
306 304
307 -bool BrCascadeClassifier::train( const string _cascadeDirName, 305 +bool BrCascadeClassifier::train(const string _cascadeDirName,
308 const string _posFilename, 306 const string _posFilename,
309 const string _negFilename, 307 const string _negFilename,
310 int _numPos, int _numNeg, 308 int _numPos, int _numNeg,
@@ -598,16 +596,13 @@ void BrCascadeClassifier::save( const string filename, bool baseFormat ) @@ -598,16 +596,13 @@ void BrCascadeClassifier::save( const string filename, bool baseFormat )
598 bool BrCascadeClassifier::load( const string cascadeDirName ) 596 bool BrCascadeClassifier::load( const string cascadeDirName )
599 { 597 {
600 FileStorage fs( cascadeDirName + CC_PARAMS_FILENAME, FileStorage::READ ); 598 FileStorage fs( cascadeDirName + CC_PARAMS_FILENAME, FileStorage::READ );
601 - qDebug() << "1";  
602 - if ( !fs.isOpened() ) {  
603 - qDebug() << "2"; 599 + if ( !fs.isOpened() )
604 return false; 600 return false;
605 - } 601 +
606 FileNode node = fs.getFirstTopLevelNode(); 602 FileNode node = fs.getFirstTopLevelNode();
607 - if ( !readParams( node ) ) {  
608 - qDebug() << "3"; 603 + if ( !readParams( node ) )
609 return false; 604 return false;
610 - } 605 +
611 featureEvaluator = FeatureEvaluator::create(cascadeParams.featureType); 606 featureEvaluator = FeatureEvaluator::create(cascadeParams.featureType);
612 featureEvaluator->init( ((FeatureParams*)featureParams), numPos + numNeg, cascadeParams.winSize ); 607 featureEvaluator->init( ((FeatureParams*)featureParams), numPos + numNeg, cascadeParams.winSize );
613 fs.release(); 608 fs.release();
@@ -646,5 +641,5 @@ void BrCascadeClassifier::getUsedFeaturesIdxMap( Mat&amp; featureMap ) @@ -646,5 +641,5 @@ void BrCascadeClassifier::getUsedFeaturesIdxMap( Mat&amp; featureMap )
646 for( int fi = 0, idx = 0; fi < varCount; fi++ ) 641 for( int fi = 0, idx = 0; fi < varCount; fi++ )
647 if ( featureMap.at<int>(0, fi) >= 0 ) 642 if ( featureMap.at<int>(0, fi) >= 0 )
648 featureMap.ptr<int>(0)[fi] = idx++; 643 featureMap.ptr<int>(0)[fi] = idx++;
649 -} 644 +}*/
650 645
openbr/core/cascade.h
@@ -8,7 +8,7 @@ @@ -8,7 +8,7 @@
8 8
9 namespace br 9 namespace br
10 { 10 {
11 - 11 +/*
12 class CascadeImageReader 12 class CascadeImageReader
13 { 13 {
14 public: 14 public:
@@ -113,7 +113,7 @@ private: @@ -113,7 +113,7 @@ private:
113 int numStages, curNumSamples; 113 int numStages, curNumSamples;
114 int numPos, numNeg; 114 int numPos, numNeg;
115 }; 115 };
116 - 116 +*/
117 } // namespace br 117 } // namespace br
118 118
119 #endif // CASCADE_H 119 #endif // CASCADE_H
openbr/openbr_plugin.cpp
@@ -1505,7 +1505,7 @@ Transform *Transform::make(QString str, QObject *parent) @@ -1505,7 +1505,7 @@ Transform *Transform::make(QString str, QObject *parent)
1505 Transform *Transform::clone() const 1505 Transform *Transform::clone() const
1506 { 1506 {
1507 Transform *clone = Factory<Transform>::make("."+description(false)); 1507 Transform *clone = Factory<Transform>::make("."+description(false));
1508 - return clone; 1508 + return clone;
1509 } 1509 }
1510 1510
1511 static void _project(const Transform *transform, const Template *src, Template *dst) 1511 static void _project(const Transform *transform, const Template *src, Template *dst)
openbr/openbr_plugin.h
@@ -1415,6 +1415,9 @@ public: @@ -1415,6 +1415,9 @@ public:
1415 virtual ~Classifier() {} 1415 virtual ~Classifier() {}
1416 1416
1417 static Classifier *make(QString str, QObject *parent); /*!< \brief Make a classifier from a string. */ 1417 static Classifier *make(QString str, QObject *parent); /*!< \brief Make a classifier from a string. */
  1418 +
  1419 + virtual Classifier *clone() const { return Factory<Classifier>::make("." + description(false)); }
  1420 +
1418 virtual void train(const QList<cv::Mat> &images, const QList<float> &labels) = 0; 1421 virtual void train(const QList<cv::Mat> &images, const QList<float> &labels) = 0;
1419 // By convention, classify should return a value normalized such that the threshold is 0. Negative values 1422 // By convention, classify should return a value normalized such that the threshold is 0. Negative values
1420 // can be interpreted as a negative classification and positive values as a positive classification. 1423 // can be interpreted as a negative classification and positive values as a positive classification.
openbr/plugins/classification/boost.cpp 0 → 100644
  1 +#include <openbr/plugins/openbr_internal.h>
  2 +#include <openbr/core/boost.h>
  3 +#include <openbr/core/features.h>
  4 +
  5 +using namespace cv;
  6 +
  7 +namespace br
  8 +{
  9 +
  10 +class BoostClassifier : public Classifier
  11 +{
  12 + Q_OBJECT
  13 +
  14 + Q_PROPERTY(float minTAR READ get_minTAR WRITE set_minTAR RESET reset_minTAR STORED false)
  15 + Q_PROPERTY(float maxFAR READ get_maxFAR WRITE set_maxFAR RESET reset_maxFAR STORED false)
  16 + Q_PROPERTY(float trimRate READ get_trimRate WRITE set_trimRate RESET reset_trimRate STORED false)
  17 + Q_PROPERTY(int maxDepth READ get_maxDepth WRITE set_maxDepth RESET reset_maxDepth STORED false)
  18 + Q_PROPERTY(int maxWeakCount READ get_maxWeakCount WRITE set_maxWeakCount RESET reset_maxWeakCount STORED false)
  19 +
  20 + BR_PROPERTY(float, minTAR, 0.995)
  21 + BR_PROPERTY(float, maxFAR, 0.5)
  22 + BR_PROPERTY(float, trimRate, 0.95)
  23 + BR_PROPERTY(int, maxDepth, 1)
  24 + BR_PROPERTY(int, maxWeakCount, 100)
  25 +
  26 + CascadeBoost *boost;
  27 +
  28 + void train(const QList<Mat> &images, const QList<float> &labels)
  29 + {
  30 + FeatureEvaluator *featureEvaluator = FeatureEvaluator::create(0);
  31 + featureEvaluator->init(new FeatureParams, images.size(), Size(24, 24));
  32 + for (int i = 0; i < images.size(); i++)
  33 + featureEvaluator->setImage(images[i], (uchar)labels[i], i);
  34 +
  35 + CascadeBoostParams stageParams(CvBoost::GENTLE, minTAR, maxFAR, trimRate, maxDepth, maxWeakCount);
  36 +
  37 + if (boost) delete boost;
  38 +
  39 + boost = new CascadeBoost;
  40 + boost->train(featureEvaluator, images.size(), 1024, 1024, stageParams);
  41 + }
  42 +
  43 + float classify(const Mat &image) const
  44 + {
  45 + (void) image;
  46 + return 0.;
  47 + }
  48 +};
  49 +
  50 +BR_REGISTER(Classifier, BoostClassifier)
  51 +
  52 +} // namespace br
  53 +
  54 +#include "classification/boost.moc"
openbr/plugins/classification/cascade.cpp 0 → 100644
  1 +#include <openbr/plugins/openbr_internal.h>
  2 +#include <openbr/core/cascade.h>
  3 +
  4 +using namespace cv;
  5 +
  6 +namespace br
  7 +{
  8 +
  9 +class CascadeClassifier : public Classifier
  10 +{
  11 + Q_OBJECT
  12 +
  13 + Q_PROPERTY(br::Classifier *stage READ get_stage WRITE set_stage RESET reset_stage STORED false)
  14 + Q_PROPERTY(int numStages READ get_numStages WRITE set_numStages RESET reset_numStages STORED false)
  15 + Q_PROPERTY(int numNegs READ get_numNegs WRITE set_numNegs RESET reset_numNegs STORED false)
  16 + Q_PROPERTY(float maxFAR READ get_maxFAR WRITE set_maxFAR RESET reset_maxFAR STORED false)
  17 +
  18 + BR_PROPERTY(br::Classifier *, stage, NULL)
  19 + BR_PROPERTY(int, numStages, 20)
  20 + BR_PROPERTY(int, numNegs, 1000)
  21 + BR_PROPERTY(float, maxFAR, pow(0.5, numStages))
  22 +
  23 + QList<Classifier *> stages;
  24 +
  25 + void train(const QList<Mat> &images, const QList<float> &labels)
  26 + {
  27 + QList<Mat> posImages, negImages;
  28 + for (int i = 0; i < images.size(); i++)
  29 + labels[i] == 1 ? posImages.append(images[i]) : negImages.append(images[i]);
  30 +
  31 + QList<Mat> trainingImages;
  32 + QList<float> trainingLabels;
  33 +
  34 + for (int i = 0; i < numStages; i++) {
  35 + float currFAR = updateTrainingSet(posImages, negImages, trainingImages, trainingLabels);
  36 +
  37 + if (currFAR < maxFAR) {
  38 + qDebug() << "FAR is below required level! Terminating early";
  39 + return;
  40 + }
  41 +
  42 + Classifier *next_stage = stage->clone();
  43 + next_stage->train(trainingImages, trainingLabels);
  44 + stages.append(next_stage);
  45 + }
  46 + }
  47 +
  48 + float classify(const Mat &image) const
  49 + {
  50 + (void) image;
  51 + return 0.;
  52 + }
  53 +
  54 + float updateTrainingSet(const QList<Mat> &posImages, const QList<Mat> &negImages, QList<Mat> &trainingImages, QList<float> &trainingLabels)
  55 + {
  56 + trainingImages.clear();
  57 + trainingLabels.clear();
  58 +
  59 + foreach (const Mat &pos, posImages) {
  60 + if (classify(pos) > 0) {
  61 + trainingImages.append(pos);
  62 + trainingLabels.append(1.);
  63 + }
  64 + }
  65 +
  66 + NegFinder finder(negImages, Size(24, 24));
  67 + int totalNegs = 0, passedNegs = 0;
  68 + while (true) {
  69 + totalNegs++;
  70 + Mat neg = finder.get();
  71 + if (classify(neg) > 0) {
  72 + trainingImages.append(neg);
  73 + trainingLabels.append(0.);
  74 + passedNegs++;
  75 + }
  76 +
  77 + if (passedNegs >= numNegs)
  78 + return passedNegs / (float)totalNegs;
  79 + }
  80 + }
  81 +
  82 +private:
  83 + struct NegFinder
  84 + {
  85 + NegFinder(const QList<Mat> &_negs, Size _winSize)
  86 + {
  87 + negs = _negs;
  88 + winSize = _winSize;
  89 +
  90 + negIdx = round = 0;
  91 + img.create( 0, 0, CV_8UC1 );
  92 + point = offset = Point( 0, 0 );
  93 + scale = 1.0F;
  94 + scaleFactor = 1.4142135623730950488016887242097F;
  95 + stepFactor = 0.5F;
  96 + }
  97 +
  98 + void _next()
  99 + {
  100 + src = negs[negIdx++];
  101 +
  102 + round += negIdx / negs.size();
  103 + round %= (winSize.width * winSize.height);
  104 + negIdx %= negs.size();
  105 +
  106 + point = offset = Point(std::min(round % winSize.width, src.cols - winSize.width),
  107 + std::min(round / winSize.width, src.rows - winSize.height));
  108 +
  109 + scale = max(((float)winSize.width + point.x) / ((float)src.cols),
  110 + ((float)winSize.height + point.y) / ((float)src.rows));
  111 +
  112 + Size sz((int)(scale*src.cols + 0.5F), (int)(scale*src.rows + 0.5F));
  113 + resize(src, img, sz);
  114 + }
  115 +
  116 + Mat get()
  117 + {
  118 + if (img.empty())
  119 + _next();
  120 +
  121 + Mat neg(winSize, CV_8UC1);
  122 + Mat m(winSize.height, winSize.width, CV_8UC1, (void*)(img.data + point.y * img.step + point.x * img.elemSize()), img.step);
  123 + m.copyTo(neg);
  124 +
  125 + if ((int)(point.x + (1.0F + stepFactor ) * winSize.width) < img.cols)
  126 + point.x += (int)(stepFactor * winSize.width);
  127 + else {
  128 + point.x = offset.x;
  129 + if ((int)( point.y + (1.0F + stepFactor ) * winSize.height ) < img.rows)
  130 + point.y += (int)(stepFactor * winSize.height);
  131 + else {
  132 + point.y = offset.y;
  133 + scale *= scaleFactor;
  134 + if( scale <= 1.0F )
  135 + resize(src, img, Size( (int)(scale*src.cols), (int)(scale*src.rows)));
  136 + else
  137 + _next();
  138 + }
  139 + }
  140 + return neg;
  141 + }
  142 +
  143 + QList<Mat> negs;
  144 + int negIdx, round;
  145 + Mat src, img;
  146 + float scale;
  147 + float scaleFactor;
  148 + float stepFactor;
  149 + Size winSize;
  150 + Point offset, point;
  151 + };
  152 +};
  153 +
  154 +BR_REGISTER(Classifier, CascadeClassifier)
  155 +
  156 +} // namespace br
  157 +
  158 +#include "classification/cascade.moc"
openbr/plugins/imgproc/resizefilter.cpp 0 → 100644
  1 +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  2 + * Copyright 2012 The MITRE Corporation *
  3 + * *
  4 + * Licensed under the Apache License, Version 2.0 (the "License"); *
  5 + * you may not use this file except in compliance with the License. *
  6 + * You may obtain a copy of the License at *
  7 + * *
  8 + * http://www.apache.org/licenses/LICENSE-2.0 *
  9 + * *
  10 + * Unless required by applicable law or agreed to in writing, software *
  11 + * distributed under the License is distributed on an "AS IS" BASIS, *
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
  13 + * See the License for the specific language governing permissions and *
  14 + * limitations under the License. *
  15 + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  16 +
  17 +#include <opencv2/imgproc/imgproc.hpp>
  18 +
  19 +#include <openbr/plugins/openbr_internal.h>
  20 +
  21 +using namespace cv;
  22 +
  23 +namespace br
  24 +{
  25 +
  26 +/*!
  27 + * \ingroup transforms
  28 + * \brief Resize the template depending on its metadata
  29 + * \author Jordan Cheney \cite JordanCheney
  30 + * \note Method: Area should be used for shrinking an image, Cubic for slow but accurate enlargment, Bilin for fast enlargement.
  31 + */
  32 +class ResizeFilterTransform : public UntrainableTransform
  33 +{
  34 + Q_OBJECT
  35 + Q_ENUMS(Method)
  36 +
  37 +public:
  38 + /*!< */
  39 + enum Method { Near = INTER_NEAREST,
  40 + Area = INTER_AREA,
  41 + Bilin = INTER_LINEAR,
  42 + Cubic = INTER_CUBIC,
  43 + Lanczo = INTER_LANCZOS4};
  44 +
  45 +private:
  46 + Q_PROPERTY(int rows READ get_rows WRITE set_rows RESET reset_rows STORED false)
  47 + Q_PROPERTY(int columns READ get_columns WRITE set_columns RESET reset_columns STORED false)
  48 + Q_PROPERTY(Method method READ get_method WRITE set_method RESET reset_method STORED false)
  49 + Q_PROPERTY(QString filterKey READ get_filterKey WRITE set_filterKey RESET reset_filterKey STORED false)
  50 + Q_PROPERTY(QString filterVal READ get_filterVal WRITE set_filterVal RESET reset_filterVal STORED false)
  51 + BR_PROPERTY(int, rows, -1)
  52 + BR_PROPERTY(int, columns, -1)
  53 + BR_PROPERTY(Method, method, Bilin)
  54 + BR_PROPERTY(QString, filterKey, "Label")
  55 + BR_PROPERTY(QString, filterVal, "1.0")
  56 +
  57 + void project(const Template &src, Template &dst) const
  58 + {
  59 + dst = src;
  60 + if (src.file.get<QString>(filterKey) == filterVal)
  61 + resize(src, dst, Size((columns == -1) ? src.m().cols*rows/src.m().rows : columns, rows), 0, 0, method);
  62 + }
  63 +};
  64 +
  65 +BR_REGISTER(Transform, ResizeFilterTransform)
  66 +
  67 +} // namespace br
  68 +
  69 +#include "imgproc/resizefilter.moc"
openbr/plugins/imgproc/slidingwindow.cpp
@@ -22,144 +22,42 @@ using namespace cv; @@ -22,144 +22,42 @@ using namespace cv;
22 namespace br 22 namespace br
23 { 23 {
24 24
25 -// Find avg aspect ratio  
26 -static float getAspectRatio(const TemplateList &data)  
27 -{  
28 - double tempRatio = 0;  
29 - int ratioCnt = 0;  
30 -  
31 - foreach (const Template &tmpl, data) {  
32 - QList<Rect> posRects = OpenCVUtils::toRects(tmpl.file.rects());  
33 - foreach (const Rect &posRect, posRects) {  
34 - if (posRect.x + posRect.width >= tmpl.m().cols || posRect.y + posRect.height >= tmpl.m().rows || posRect.x < 0 || posRect.y < 0) {  
35 - continue;  
36 - }  
37 - tempRatio += (float)posRect.width / (float)posRect.height;  
38 - ratioCnt += 1;  
39 - }  
40 - }  
41 - return tempRatio / (double)ratioCnt;  
42 -}  
43 -  
44 /*! 25 /*!
45 * \ingroup transforms 26 * \ingroup transforms
46 - * \brief Applies a transform to a sliding window. 27 + * \brief Applies a classifier to a sliding window.
47 * Discards negative detections. 28 * Discards negative detections.
48 - * \author Austin Blanton \cite imaus10 29 + * \author Jordan Cheney \cite JordanCheney
49 */ 30 */
50 class SlidingWindowTransform : public Transform 31 class SlidingWindowTransform : public Transform
51 { 32 {
52 Q_OBJECT 33 Q_OBJECT
53 - Q_PROPERTY(br::Transform *transform READ get_transform WRITE set_transform RESET reset_transform STORED false)  
54 - Q_PROPERTY(int windowWidth READ get_windowWidth WRITE set_windowWidth RESET reset_windowWidth STORED false)  
55 - Q_PROPERTY(bool takeFirst READ get_takeFirst WRITE set_takeFirst RESET reset_takeFirst STORED false)  
56 - Q_PROPERTY(float threshold READ get_threshold WRITE set_threshold RESET reset_threshold STORED false)  
57 - Q_PROPERTY(float stepFraction READ get_stepFraction WRITE set_stepFraction RESET reset_stepFraction STORED false)  
58 - Q_PROPERTY(int ignoreBorder READ get_ignoreBorder WRITE set_ignoreBorder RESET reset_ignoreBorder STORED true)  
59 - BR_PROPERTY(br::Transform *, transform, NULL)  
60 - BR_PROPERTY(int, windowWidth, 24)  
61 - BR_PROPERTY(bool, takeFirst, false)  
62 - BR_PROPERTY(float, threshold, 0)  
63 - BR_PROPERTY(float, stepFraction, 0.25)  
64 - BR_PROPERTY(int, ignoreBorder, 0) 34 + Q_PROPERTY(br::Classifier *classifier READ get_classifier WRITE set_classifier RESET reset_classifier STORED false)
  35 + Q_PROPERTY(int minSize READ get_minSize WRITE set_minSize RESET reset_minSize STORED false)
  36 + Q_PROPERTY(int maxSize READ get_maxSize WRITE set_maxSize RESET reset_maxSize STORED false)
  37 + Q_PROPERTY(float scaleFactor READ get_scaleFactor WRITE set_scaleFactor RESET reset_scaleFactor STORED false)
65 38
66 -private:  
67 - int windowHeight;  
68 - bool skipProject; 39 + BR_PROPERTY(br::Classifier *, classifier, NULL)
  40 + BR_PROPERTY(int, minSize, 20)
  41 + BR_PROPERTY(int, maxSize, -1)
  42 + BR_PROPERTY(float, scaleFactor, 1.2)
69 43
70 - void train(const TemplateList &data) 44 + void train(const TemplateList &_data)
71 { 45 {
72 - skipProject = true;  
73 - float aspectRatio = data.first().file.get<float>("aspectRatio", -1);  
74 - if (aspectRatio == -1)  
75 - aspectRatio = getAspectRatio(data);  
76 - windowHeight = qRound(windowWidth / aspectRatio); 46 + QList<Mat> data = _data.data();
  47 + QList<float> labels = File::get<float>(_data, "Label");
77 48
78 - if (transform->trainable) {  
79 - TemplateList dataOut = data;  
80 - if (ignoreBorder > 0) {  
81 - for (int i = 0; i < dataOut.size(); i++) {  
82 - Template t = dataOut[i];  
83 - Mat m = t.m();  
84 - dataOut.replace(i,Template(t.file, Mat(m,Rect(ignoreBorder,ignoreBorder,m.cols - ignoreBorder * 2, m.rows - ignoreBorder * 2))));  
85 - }  
86 - }  
87 - transform->train(dataOut);  
88 - }  
89 - }  
90 -  
91 - void store(QDataStream &stream) const  
92 - {  
93 - transform->store(stream);  
94 - stream << windowHeight;  
95 - }  
96 -  
97 - void load(QDataStream &stream)  
98 - {  
99 - transform->load(stream);  
100 - stream >> windowHeight; 49 + classifier->train(data, labels);
101 } 50 }
102 51
103 void project(const Template &src, Template &dst) const 52 void project(const Template &src, Template &dst) const
104 { 53 {
105 - float scale = src.file.get<float>("scale", 1);  
106 - projectHelp(src, dst, windowWidth, windowHeight, scale);  
107 - }  
108 -  
109 - protected:  
110 - void projectHelp(const Template &src, Template &dst, int windowWidth, int windowHeight, float scale = 1) const  
111 - {  
112 -  
113 - dst = src;  
114 - if (skipProject) {  
115 - dst = src;  
116 - return;  
117 - }  
118 -  
119 - Template windowTemplate(src.file, src);  
120 - QList<float> confidences = dst.file.getList<float>("Confidences", QList<float>());  
121 - for (float y = 0; y + windowHeight < src.m().rows; y += windowHeight*stepFraction) {  
122 - for (float x = 0; x + windowWidth < src.m().cols; x += windowWidth*stepFraction) {  
123 - Mat windowMat(src, Rect(x + ignoreBorder, y + ignoreBorder, windowWidth - ignoreBorder * 2, windowHeight - ignoreBorder * 2));  
124 - windowTemplate.replace(0,windowMat);  
125 - Template detect;  
126 - transform->project(windowTemplate, detect);  
127 - float conf = detect.m().at<float>(0);  
128 -  
129 - // the result will be in the Label  
130 - if (conf > threshold) {  
131 - dst.file.appendRect(QRectF(x*scale, y*scale, windowWidth*scale, windowHeight*scale));  
132 - confidences.append(conf);  
133 - if (takeFirst)  
134 - return;  
135 - }  
136 - }  
137 - }  
138 - dst.file.setList<float>("Confidences", confidences); 54 + (void) src;
  55 + (void) dst;
139 } 56 }
140 }; 57 };
141 58
142 BR_REGISTER(Transform, SlidingWindowTransform) 59 BR_REGISTER(Transform, SlidingWindowTransform)
143 60
144 -/*!  
145 - * \ingroup transforms  
146 - * \brief Overloads SlidingWindowTransform for integral images that should be  
147 - * sampled at multiple scales.  
148 - * \author Josh Klontz \cite jklontz  
149 - */  
150 -class IntegralSlidingWindowTransform : public SlidingWindowTransform  
151 -{  
152 - Q_OBJECT  
153 -  
154 - private:  
155 - void project(const Template &src, Template &dst) const  
156 - {  
157 - // TODO: call SlidingWindowTransform::project on multiple scales  
158 - SlidingWindowTransform::projectHelp(src, dst, 24, 24);  
159 - }  
160 -};  
161 -  
162 -BR_REGISTER(Transform, IntegralSlidingWindowTransform)  
163 61
164 } // namespace br 62 } // namespace br
165 63
openbr/plugins/metadata/cascade.cpp
@@ -16,12 +16,13 @@ @@ -16,12 +16,13 @@
16 #include <QProcess> 16 #include <QProcess>
17 #include <QTemporaryFile> 17 #include <QTemporaryFile>
18 #include <opencv2/objdetect/objdetect.hpp> 18 #include <opencv2/objdetect/objdetect.hpp>
  19 +#include <fstream>
19 20
20 #include <openbr/plugins/openbr_internal.h> 21 #include <openbr/plugins/openbr_internal.h>
21 #include <openbr/core/opencvutils.h> 22 #include <openbr/core/opencvutils.h>
22 #include <openbr/core/resource.h> 23 #include <openbr/core/resource.h>
23 #include <openbr/core/qtutils.h> 24 #include <openbr/core/qtutils.h>
24 -#include <openbr/core/cascade.h> 25 +#include <openbr/core/_cascade.h>
25 26
26 using namespace cv; 27 using namespace cv;
27 28
@@ -75,18 +76,24 @@ class CascadeTransform : public MetaTransform @@ -75,18 +76,24 @@ class CascadeTransform : public MetaTransform
75 // Training parameters 76 // Training parameters
76 Q_PROPERTY(QString vecFile READ get_vecFile WRITE set_vecFile RESET reset_vecFile STORED false) 77 Q_PROPERTY(QString vecFile READ get_vecFile WRITE set_vecFile RESET reset_vecFile STORED false)
77 Q_PROPERTY(QString negFile READ get_negFile WRITE set_negFile RESET reset_negFile STORED false) 78 Q_PROPERTY(QString negFile READ get_negFile WRITE set_negFile RESET reset_negFile STORED false)
  79 + Q_PROPERTY(int winWidth READ get_winWidth WRITE set_winWidth RESET reset_winWidth STORED false)
  80 + Q_PROPERTY(int winHeight READ get_winHeight WRITE set_winHeight RESET reset_winHeight STORED false)
78 Q_PROPERTY(int numPos READ get_numPos WRITE set_numPos RESET reset_numPos STORED false) 81 Q_PROPERTY(int numPos READ get_numPos WRITE set_numPos RESET reset_numPos STORED false)
79 Q_PROPERTY(int numNeg READ get_numNeg WRITE set_numNeg RESET reset_numNeg STORED false) 82 Q_PROPERTY(int numNeg READ get_numNeg WRITE set_numNeg RESET reset_numNeg STORED false)
  83 + Q_PROPERTY(int numStages READ get_numStages WRITE set_numStages RESET reset_numStages STORED false)
80 84
81 BR_PROPERTY(QString, model, "FrontalFace") 85 BR_PROPERTY(QString, model, "FrontalFace")
82 BR_PROPERTY(int, minSize, 64) 86 BR_PROPERTY(int, minSize, 64)
83 BR_PROPERTY(int, minNeighbors, 5) 87 BR_PROPERTY(int, minNeighbors, 5)
84 BR_PROPERTY(bool, ROCMode, false) 88 BR_PROPERTY(bool, ROCMode, false)
85 89
86 - BR_PROPERTY(QString, vecFile, "data.vec") 90 + BR_PROPERTY(QString, vecFile, "vec.vec")
87 BR_PROPERTY(QString, negFile, "neg.txt") 91 BR_PROPERTY(QString, negFile, "neg.txt")
  92 + BR_PROPERTY(int, winWidth, 24)
  93 + BR_PROPERTY(int, winHeight, 24)
88 BR_PROPERTY(int, numPos, 1000) 94 BR_PROPERTY(int, numPos, 1000)
89 BR_PROPERTY(int, numNeg, 1000) 95 BR_PROPERTY(int, numNeg, 1000)
  96 + BR_PROPERTY(int, numStages, 20)
90 97
91 Resource<CascadeClassifier> cascadeResource; 98 Resource<CascadeClassifier> cascadeResource;
92 99
@@ -97,21 +104,100 @@ class CascadeTransform : public MetaTransform @@ -97,21 +104,100 @@ class CascadeTransform : public MetaTransform
97 this->trainable = false; 104 this->trainable = false;
98 } 105 }
99 106
  107 + QList<Mat> getPos()
  108 + {
  109 + FILE *file = fopen(vecFile.toStdString().c_str(), "rb");
  110 + if ( !file )
  111 + qFatal("Couldn't open the file");
  112 +
  113 + short* vec = 0;
  114 + int count, vecSize, last, base;
  115 +
  116 + short tmp = 0;
  117 + if( fread( &count, sizeof( count ), 1, file ) != 1 ||
  118 + fread( &vecSize, sizeof( vecSize ), 1, file ) != 1 ||
  119 + fread( &tmp, sizeof( tmp ), 1, file ) != 1 ||
  120 + fread( &tmp, sizeof( tmp ), 1, file ) != 1 )
  121 + CV_Error_( CV_StsParseError, ("wrong file format for %s\n", qPrintable(vecFile)) );
  122 + base = sizeof( count ) + sizeof( vecSize ) + 2*sizeof( tmp );
  123 +
  124 + last = 0;
  125 + vec = (short*) cvAlloc( sizeof( *vec ) * vecSize );
  126 + CV_Assert( vec );
  127 +
  128 + QList<Mat> posImages;
  129 + for (int i = 0; i < 35770; i++) {
  130 + Mat pos(winHeight, winWidth, CV_8UC1);
  131 +
  132 + CV_Assert( pos.rows * pos.cols == vecSize );
  133 + uchar tmp = 0;
  134 + size_t elements_read = fread( &tmp, sizeof( tmp ), 1, file );
  135 + if( elements_read != 1 )
  136 + CV_Error( CV_StsBadArg, "Can not get new positive sample. The most possible reason is "
  137 + "insufficient count of samples in given vec-file.\n");
  138 + elements_read = fread( vec, sizeof( vec[0] ), vecSize, file );
  139 + if( elements_read != (size_t)(vecSize) )
  140 + CV_Error( CV_StsBadArg, "Can not get new positive sample. Seems that vec-file has incorrect structure.\n");
  141 +
  142 + if( feof( file ) || last++ >= count )
  143 + CV_Error( CV_StsBadArg, "Can not get new positive sample. vec-file is over.\n");
  144 +
  145 + for( int r = 0; r < pos.rows; r++ )
  146 + for( int c = 0; c < pos.cols; c++ )
  147 + pos.ptr(r)[c] = (uchar)vec[r * pos.cols + c];
  148 + posImages.append(pos);
  149 + }
  150 + return posImages;
  151 + }
  152 +
  153 + QList<Mat> getNeg()
  154 + {
  155 + QList<Mat> negs;
  156 +
  157 + std::string dirname, str;
  158 + std::ifstream file(negFile.toStdString().c_str());
  159 +
  160 + size_t pos = negFile.toStdString().rfind('\\');
  161 + char dlmrt = '\\';
  162 + if (pos == string::npos)
  163 + {
  164 + pos = negFile.toStdString().rfind('/');
  165 + dlmrt = '/';
  166 + }
  167 + dirname = pos == string::npos ? "" : negFile.toStdString().substr(0, pos) + dlmrt;
  168 + while( !file.eof() )
  169 + {
  170 + std::getline(file, str);
  171 + if (str.empty()) break;
  172 + if (str.at(0) == '#' ) continue;
  173 + negs.append(imread(dirname + str, CV_LOAD_IMAGE_GRAYSCALE));
  174 + }
  175 + file.close();
  176 +
  177 + return negs;
  178 + }
  179 +
100 // Train transform 180 // Train transform
101 void train(const TemplateList& data) 181 void train(const TemplateList& data)
102 { 182 {
103 - (void)data; 183 + (void) data;
  184 +
  185 + QList<Mat> posImages = getPos();
  186 + QList<Mat> negImages = getNeg();
104 187
105 BrCascadeClassifier classifier; 188 BrCascadeClassifier classifier;
106 189
107 CascadeParams cascadeParams(CascadeParams::BOOST, FeatureParams::LBP); 190 CascadeParams cascadeParams(CascadeParams::BOOST, FeatureParams::LBP);
  191 + cascadeParams.winSize = Size(winWidth, winHeight);
  192 +
108 CascadeBoostParams stageParams(CvBoost::GENTLE, 0.999, 0.5, 0.95, 1, 200); 193 CascadeBoostParams stageParams(CvBoost::GENTLE, 0.999, 0.5, 0.95, 1, 200);
109 LBPFeatureParams featureParams; 194 LBPFeatureParams featureParams;
110 195
111 QString cascadeDir = Globals->sdkPath + "/share/openbr/models/openbrcascades/" + model; 196 QString cascadeDir = Globals->sdkPath + "/share/openbr/models/openbrcascades/" + model;
112 classifier.train(cascadeDir.toStdString(), 197 classifier.train(cascadeDir.toStdString(),
113 - vecFile.toStdString(), negFile.toStdString(),  
114 - numPos, numNeg, 1024, 1024, 17, 198 + posImages.toVector().toStdVector(),
  199 + negImages.toVector().toStdVector(),
  200 + numPos, numNeg, 1024, 1024, numStages,
115 cascadeParams, featureParams, stageParams); 201 cascadeParams, featureParams, stageParams);
116 } 202 }
117 203