Commit 6910358f3022ba7f5996f4142d9a158a7f1cf85d
1 parent
d9fe1bd0
Step towards step2
Showing
11 changed files
with
1077 additions
and
140 deletions
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& featureMap ) | @@ -646,5 +641,5 @@ void BrCascadeClassifier::getUsedFeaturesIdxMap( Mat& 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 |