/*M/////////////////////////////////////////////////////////////////////////////////////// // // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. // // By downloading, copying, installing or using the software you agree to this license. // If you do not agree to this license, do not download, install, // copy or use the software. // // // Intel License Agreement // For Open Source Computer Vision Library // // Copyright (C) 2000, Intel Corporation, all rights reserved. // Third party copyrights are property of their respective owners. // // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: // // * Redistribution's of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // * Redistribution's in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // * The name of Intel Corporation may not be used to endorse or promote products // derived from this software without specific prior written permission. // // This software is provided by the copyright holders and contributors "as is" and // any express or implied warranties, including, but not limited to, the implied // warranties of merchantability and fitness for a particular purpose are disclaimed. // In no event shall the Intel Corporation or contributors be liable for any direct, // indirect, incidental, special, exemplary, or consequential damages // (including, but not limited to, procurement of substitute goods or services; // loss of use, data, or profits; or business interruption) however caused // and on any theory of liability, whether in contract, strict liability, // or tort (including negligence or otherwise) arising in any way out of // the use of this software, even if advised of the possibility of such damage. // //M*/ /* * cvhaarclassifier.cpp * * haar classifiers (stump, CART, stage, cascade) */ #include "_cvhaartraining.h" CvIntHaarClassifier* icvCreateCARTHaarClassifier( int count ) { CvCARTHaarClassifier* cart; size_t datasize; datasize = sizeof( *cart ) + ( sizeof( int ) + sizeof( CvTHaarFeature ) + sizeof( CvFastHaarFeature ) + sizeof( float ) + sizeof( int ) + sizeof( int ) ) * count + sizeof( float ) * (count + 1); cart = (CvCARTHaarClassifier*) cvAlloc( datasize ); memset( cart, 0, datasize ); cart->feature = (CvTHaarFeature*) (cart + 1); cart->fastfeature = (CvFastHaarFeature*) (cart->feature + count); cart->threshold = (float*) (cart->fastfeature + count); cart->left = (int*) (cart->threshold + count); cart->right = (int*) (cart->left + count); cart->val = (float*) (cart->right + count); cart->compidx = (int*) (cart->val + count + 1 ); cart->count = count; cart->eval = icvEvalCARTHaarClassifier; cart->save = icvSaveCARTHaarClassifier; cart->release = icvReleaseHaarClassifier; return (CvIntHaarClassifier*) cart; } void icvReleaseHaarClassifier( CvIntHaarClassifier** classifier ) { cvFree( classifier ); *classifier = NULL; } void icvInitCARTHaarClassifier( CvCARTHaarClassifier* carthaar, CvCARTClassifier* cart, CvIntHaarFeatures* intHaarFeatures ) { int i; for( i = 0; i < cart->count; i++ ) { carthaar->feature[i] = intHaarFeatures->feature[cart->compidx[i]]; carthaar->fastfeature[i] = intHaarFeatures->fastfeature[cart->compidx[i]]; carthaar->threshold[i] = cart->threshold[i]; carthaar->left[i] = cart->left[i]; carthaar->right[i] = cart->right[i]; carthaar->val[i] = cart->val[i]; carthaar->compidx[i] = cart->compidx[i]; } carthaar->count = cart->count; carthaar->val[cart->count] = cart->val[cart->count]; } float icvEvalCARTHaarClassifier( CvIntHaarClassifier* classifier, sum_type* sum, sum_type* tilted, float normfactor ) { int idx = 0; do { if( cvEvalFastHaarFeature( ((CvCARTHaarClassifier*) classifier)->fastfeature + idx, sum, tilted ) < (((CvCARTHaarClassifier*) classifier)->threshold[idx] * normfactor) ) { idx = ((CvCARTHaarClassifier*) classifier)->left[idx]; } else { idx = ((CvCARTHaarClassifier*) classifier)->right[idx]; } } while( idx > 0 ); return ((CvCARTHaarClassifier*) classifier)->val[-idx]; } CvIntHaarClassifier* icvCreateStageHaarClassifier( int count, float threshold ) { CvStageHaarClassifier* stage; size_t datasize; datasize = sizeof( *stage ) + sizeof( CvIntHaarClassifier* ) * count; stage = (CvStageHaarClassifier*) cvAlloc( datasize ); memset( stage, 0, datasize ); stage->count = count; stage->threshold = threshold; stage->classifier = (CvIntHaarClassifier**) (stage + 1); stage->eval = icvEvalStageHaarClassifier; stage->save = icvSaveStageHaarClassifier; stage->release = icvReleaseStageHaarClassifier; return (CvIntHaarClassifier*) stage; } void icvReleaseStageHaarClassifier( CvIntHaarClassifier** classifier ) { int i; for( i = 0; i < ((CvStageHaarClassifier*) *classifier)->count; i++ ) { if( ((CvStageHaarClassifier*) *classifier)->classifier[i] != NULL ) { ((CvStageHaarClassifier*) *classifier)->classifier[i]->release( &(((CvStageHaarClassifier*) *classifier)->classifier[i]) ); } } cvFree( classifier ); *classifier = NULL; } float icvEvalStageHaarClassifier( CvIntHaarClassifier* classifier, sum_type* sum, sum_type* tilted, float normfactor ) { int i; float stage_sum; stage_sum = 0.0F; for( i = 0; i < ((CvStageHaarClassifier*) classifier)->count; i++ ) { stage_sum += ((CvStageHaarClassifier*) classifier)->classifier[i]->eval( ((CvStageHaarClassifier*) classifier)->classifier[i], sum, tilted, normfactor ); } return stage_sum; } CvIntHaarClassifier* icvCreateCascadeHaarClassifier( int count ) { CvCascadeHaarClassifier* ptr; size_t datasize; datasize = sizeof( *ptr ) + sizeof( CvIntHaarClassifier* ) * count; ptr = (CvCascadeHaarClassifier*) cvAlloc( datasize ); memset( ptr, 0, datasize ); ptr->count = count; ptr->classifier = (CvIntHaarClassifier**) (ptr + 1); ptr->eval = icvEvalCascadeHaarClassifier; ptr->save = NULL; ptr->release = icvReleaseCascadeHaarClassifier; return (CvIntHaarClassifier*) ptr; } void icvReleaseCascadeHaarClassifier( CvIntHaarClassifier** classifier ) { int i; for( i = 0; i < ((CvCascadeHaarClassifier*) *classifier)->count; i++ ) { if( ((CvCascadeHaarClassifier*) *classifier)->classifier[i] != NULL ) { ((CvCascadeHaarClassifier*) *classifier)->classifier[i]->release( &(((CvCascadeHaarClassifier*) *classifier)->classifier[i]) ); } } cvFree( classifier ); *classifier = NULL; } float icvEvalCascadeHaarClassifier( CvIntHaarClassifier* classifier, sum_type* sum, sum_type* tilted, float normfactor ) { int i; for( i = 0; i < ((CvCascadeHaarClassifier*) classifier)->count; i++ ) { if( ((CvCascadeHaarClassifier*) classifier)->classifier[i]->eval( ((CvCascadeHaarClassifier*) classifier)->classifier[i], sum, tilted, normfactor ) < ( ((CvStageHaarClassifier*) ((CvCascadeHaarClassifier*) classifier)->classifier[i])->threshold - CV_THRESHOLD_EPS) ) { return 0.0; } } return 1.0; } void icvSaveHaarFeature( CvTHaarFeature* feature, FILE* file ) { fprintf( file, "%d\n", ( ( feature->rect[2].weight == 0.0F ) ? 2 : 3) ); fprintf( file, "%d %d %d %d %d %d\n", feature->rect[0].r.x, feature->rect[0].r.y, feature->rect[0].r.width, feature->rect[0].r.height, 0, (int) (feature->rect[0].weight) ); fprintf( file, "%d %d %d %d %d %d\n", feature->rect[1].r.x, feature->rect[1].r.y, feature->rect[1].r.width, feature->rect[1].r.height, 0, (int) (feature->rect[1].weight) ); if( feature->rect[2].weight != 0.0F ) { fprintf( file, "%d %d %d %d %d %d\n", feature->rect[2].r.x, feature->rect[2].r.y, feature->rect[2].r.width, feature->rect[2].r.height, 0, (int) (feature->rect[2].weight) ); } fprintf( file, "%s\n", &(feature->desc[0]) ); } void icvLoadHaarFeature( CvTHaarFeature* feature, FILE* file ) { int nrect; int j; int tmp; int weight; nrect = 0; int values_read = fscanf( file, "%d", &nrect ); CV_Assert(values_read == 1); assert( nrect <= CV_HAAR_FEATURE_MAX ); for( j = 0; j < nrect; j++ ) { values_read = fscanf( file, "%d %d %d %d %d %d", &(feature->rect[j].r.x), &(feature->rect[j].r.y), &(feature->rect[j].r.width), &(feature->rect[j].r.height), &tmp, &weight ); CV_Assert(values_read == 6); feature->rect[j].weight = (float) weight; } for( j = nrect; j < CV_HAAR_FEATURE_MAX; j++ ) { feature->rect[j].r.x = 0; feature->rect[j].r.y = 0; feature->rect[j].r.width = 0; feature->rect[j].r.height = 0; feature->rect[j].weight = 0.0f; } values_read = fscanf( file, "%s", &(feature->desc[0]) ); CV_Assert(values_read == 1); feature->tilted = ( feature->desc[0] == 't' ); } void icvSaveCARTHaarClassifier( CvIntHaarClassifier* classifier, FILE* file ) { int i; int count; count = ((CvCARTHaarClassifier*) classifier)->count; fprintf( file, "%d\n", count ); for( i = 0; i < count; i++ ) { icvSaveHaarFeature( &(((CvCARTHaarClassifier*) classifier)->feature[i]), file ); fprintf( file, "%e %d %d\n", ((CvCARTHaarClassifier*) classifier)->threshold[i], ((CvCARTHaarClassifier*) classifier)->left[i], ((CvCARTHaarClassifier*) classifier)->right[i] ); } for( i = 0; i <= count; i++ ) { fprintf( file, "%e ", ((CvCARTHaarClassifier*) classifier)->val[i] ); } fprintf( file, "\n" ); } CvIntHaarClassifier* icvLoadCARTHaarClassifier( FILE* file, int step ) { CvCARTHaarClassifier* ptr; int i; int count; ptr = NULL; int values_read = fscanf( file, "%d", &count ); CV_Assert(values_read == 1); if( count > 0 ) { ptr = (CvCARTHaarClassifier*) icvCreateCARTHaarClassifier( count ); for( i = 0; i < count; i++ ) { icvLoadHaarFeature( &(ptr->feature[i]), file ); values_read = fscanf( file, "%f %d %d", &(ptr->threshold[i]), &(ptr->left[i]), &(ptr->right[i]) ); CV_Assert(values_read == 3); } for( i = 0; i <= count; i++ ) { values_read = fscanf( file, "%f", &(ptr->val[i]) ); CV_Assert(values_read == 1); } icvConvertToFastHaarFeature( ptr->feature, ptr->fastfeature, ptr->count, step ); } return (CvIntHaarClassifier*) ptr; } void icvSaveStageHaarClassifier( CvIntHaarClassifier* classifier, FILE* file ) { int count; int i; float threshold; count = ((CvStageHaarClassifier*) classifier)->count; fprintf( file, "%d\n", count ); for( i = 0; i < count; i++ ) { ((CvStageHaarClassifier*) classifier)->classifier[i]->save( ((CvStageHaarClassifier*) classifier)->classifier[i], file ); } threshold = ((CvStageHaarClassifier*) classifier)->threshold; /* to be compatible with the previous implementation */ /* threshold = 2.0F * ((CvStageHaarClassifier*) classifier)->threshold - count; */ fprintf( file, "%e\n", threshold ); } static CvIntHaarClassifier* icvLoadCARTStageHaarClassifierF( FILE* file, int step ) { CvStageHaarClassifier* ptr = NULL; //CV_FUNCNAME( "icvLoadCARTStageHaarClassifierF" ); __BEGIN__; if( file != NULL ) { int count; int i; float threshold; count = 0; int values_read = fscanf( file, "%d", &count ); CV_Assert(values_read == 1); if( count > 0 ) { ptr = (CvStageHaarClassifier*) icvCreateStageHaarClassifier( count, 0.0F ); for( i = 0; i < count; i++ ) { ptr->classifier[i] = icvLoadCARTHaarClassifier( file, step ); } values_read = fscanf( file, "%f", &threshold ); CV_Assert(values_read == 1); ptr->threshold = threshold; /* to be compatible with the previous implementation */ /* ptr->threshold = 0.5F * (threshold + count); */ } if( feof( file ) ) { ptr->release( (CvIntHaarClassifier**) &ptr ); ptr = NULL; } } __END__; return (CvIntHaarClassifier*) ptr; } CvIntHaarClassifier* icvLoadCARTStageHaarClassifier( const char* filename, int step ) { CvIntHaarClassifier* ptr = NULL; CV_FUNCNAME( "icvLoadCARTStageHaarClassifier" ); __BEGIN__; FILE* file; file = fopen( filename, "r" ); if( file ) { CV_CALL( ptr = icvLoadCARTStageHaarClassifierF( file, step ) ); fclose( file ); } __END__; return ptr; } /* tree cascade classifier */ /* evaluates a tree cascade classifier */ float icvEvalTreeCascadeClassifier( CvIntHaarClassifier* classifier, sum_type* sum, sum_type* tilted, float normfactor ) { CvTreeCascadeNode* ptr; ptr = ((CvTreeCascadeClassifier*) classifier)->root; while( ptr ) { if( ptr->stage->eval( (CvIntHaarClassifier*) ptr->stage, sum, tilted, normfactor ) >= ptr->stage->threshold - CV_THRESHOLD_EPS ) { ptr = ptr->child; } else { while( ptr && ptr->next == NULL ) ptr = ptr->parent; if( ptr == NULL ) return 0.0F; ptr = ptr->next; } } return 1.0F; } /* sets path int the tree form the root to the leaf node */ void icvSetLeafNode( CvTreeCascadeClassifier* tcc, CvTreeCascadeNode* leaf ) { CV_FUNCNAME( "icvSetLeafNode" ); __BEGIN__; CvTreeCascadeNode* ptr; ptr = NULL; while( leaf ) { leaf->child_eval = ptr; ptr = leaf; leaf = leaf->parent; } leaf = tcc->root; while( leaf && leaf != ptr ) leaf = leaf->next; if( leaf != ptr ) CV_ERROR( CV_StsError, "Invalid tcc or leaf node." ); tcc->root_eval = ptr; __END__; } /* evaluates a tree cascade classifier. used in filtering */ float icvEvalTreeCascadeClassifierFilter( CvIntHaarClassifier* classifier, sum_type* sum, sum_type* tilted, float normfactor ) { CvTreeCascadeNode* ptr; CvTreeCascadeClassifier* tree; tree = (CvTreeCascadeClassifier*) classifier; ptr = ((CvTreeCascadeClassifier*) classifier)->root_eval; while( ptr ) { if( ptr->stage->eval( (CvIntHaarClassifier*) ptr->stage, sum, tilted, normfactor ) < ptr->stage->threshold - CV_THRESHOLD_EPS ) { return 0.0F; } ptr = ptr->child_eval; } return 1.0F; } /* creates tree cascade node */ CvTreeCascadeNode* icvCreateTreeCascadeNode() { CvTreeCascadeNode* ptr = NULL; CV_FUNCNAME( "icvCreateTreeCascadeNode" ); __BEGIN__; size_t data_size; data_size = sizeof( *ptr ); CV_CALL( ptr = (CvTreeCascadeNode*) cvAlloc( data_size ) ); memset( ptr, 0, data_size ); __END__; return ptr; } /* releases all tree cascade nodes accessible via links */ void icvReleaseTreeCascadeNodes( CvTreeCascadeNode** node ) { //CV_FUNCNAME( "icvReleaseTreeCascadeNodes" ); __BEGIN__; if( node && *node ) { CvTreeCascadeNode* ptr; CvTreeCascadeNode* ptr_; ptr = *node; while( ptr ) { while( ptr->child ) ptr = ptr->child; if( ptr->stage ) ptr->stage->release( (CvIntHaarClassifier**) &ptr->stage ); ptr_ = ptr; while( ptr && ptr->next == NULL ) ptr = ptr->parent; if( ptr ) ptr = ptr->next; cvFree( &ptr_ ); } } __END__; } /* releases tree cascade classifier */ void icvReleaseTreeCascadeClassifier( CvIntHaarClassifier** classifier ) { if( classifier && *classifier ) { icvReleaseTreeCascadeNodes( &((CvTreeCascadeClassifier*) *classifier)->root ); cvFree( classifier ); *classifier = NULL; } } void icvPrintTreeCascade( CvTreeCascadeNode* root ) { //CV_FUNCNAME( "icvPrintTreeCascade" ); __BEGIN__; CvTreeCascadeNode* node; CvTreeCascadeNode* n; char buf0[256]; char buf[256]; int level; int i; int max_level; node = root; level = max_level = 0; while( node ) { while( node->child ) { node = node->child; level++; } if( level > max_level ) { max_level = level; } while( node && !node->next ) { node = node->parent; level--; } if( node ) node = node->next; } printf( "\nTree Classifier\n" ); printf( "Stage\n" ); for( i = 0; i <= max_level; i++ ) printf( "+---" ); printf( "+\n" ); for( i = 0; i <= max_level; i++ ) printf( "|%3d", i ); printf( "|\n" ); for( i = 0; i <= max_level; i++ ) printf( "+---" ); printf( "+\n\n" ); node = root; buf[0] = 0; while( node ) { sprintf( buf + strlen( buf ), "%3d", node->idx ); while( node->child ) { node = node->child; sprintf( buf + strlen( buf ), ((node->idx < 10) ? "---%d" : ((node->idx < 100) ? "--%d" : "-%d")), node->idx ); } printf( " %s\n", buf ); while( node && !node->next ) { node = node->parent; } if( node ) { node = node->next; n = node->parent; buf[0] = 0; while( n ) { if( n->next ) sprintf( buf0, " | %s", buf ); else sprintf( buf0, " %s", buf ); strcpy( buf, buf0 ); n = n->parent; } printf( " %s |\n", buf ); } } printf( "\n" ); fflush( stdout ); __END__; } CvIntHaarClassifier* icvLoadTreeCascadeClassifier( const char* filename, int step, int* splits ) { CvTreeCascadeClassifier* ptr = NULL; CvTreeCascadeNode** nodes = NULL; CV_FUNCNAME( "icvLoadTreeCascadeClassifier" ); __BEGIN__; size_t data_size; CvStageHaarClassifier* stage; char stage_name[PATH_MAX]; char* suffix; int i, num; FILE* f; int result, parent=0, next=0; int stub; if( !splits ) splits = &stub; *splits = 0; data_size = sizeof( *ptr ); CV_CALL( ptr = (CvTreeCascadeClassifier*) cvAlloc( data_size ) ); memset( ptr, 0, data_size ); ptr->eval = icvEvalTreeCascadeClassifier; ptr->release = icvReleaseTreeCascadeClassifier; sprintf( stage_name, "%s/", filename ); suffix = stage_name + strlen( stage_name ); for( i = 0; ; i++ ) { sprintf( suffix, "%d/%s", i, CV_STAGE_CART_FILE_NAME ); f = fopen( stage_name, "r" ); if( !f ) break; fclose( f ); } num = i; if( num < 1 ) EXIT; data_size = sizeof( *nodes ) * num; CV_CALL( nodes = (CvTreeCascadeNode**) cvAlloc( data_size ) ); for( i = 0; i < num; i++ ) { sprintf( suffix, "%d/%s", i, CV_STAGE_CART_FILE_NAME ); f = fopen( stage_name, "r" ); CV_CALL( stage = (CvStageHaarClassifier*) icvLoadCARTStageHaarClassifierF( f, step ) ); result = ( f && stage ) ? fscanf( f, "%d%d", &parent, &next ) : 0; if( f ) fclose( f ); if( result != 2 ) { num = i; break; } printf( "Stage %d loaded\n", i ); if( parent >= i || (next != -1 && next != i + 1) ) CV_ERROR( CV_StsError, "Invalid tree links" ); CV_CALL( nodes[i] = icvCreateTreeCascadeNode() ); nodes[i]->stage = stage; nodes[i]->idx = i; nodes[i]->parent = (parent != -1 ) ? nodes[parent] : NULL; nodes[i]->next = ( next != -1 ) ? nodes[i] : NULL; nodes[i]->child = NULL; } for( i = 0; i < num; i++ ) { if( nodes[i]->next ) { (*splits)++; nodes[i]->next = nodes[i+1]; } if( nodes[i]->parent && nodes[i]->parent->child == NULL ) { nodes[i]->parent->child = nodes[i]; } } ptr->root = nodes[0]; ptr->next_idx = num; __END__; cvFree( &nodes ); return (CvIntHaarClassifier*) ptr; } CvTreeCascadeNode* icvFindDeepestLeaves( CvTreeCascadeClassifier* tcc ) { CvTreeCascadeNode* leaves; //CV_FUNCNAME( "icvFindDeepestLeaves" ); __BEGIN__; int level, cur_level; CvTreeCascadeNode* ptr; CvTreeCascadeNode* last; leaves = last = NULL; ptr = tcc->root; level = -1; cur_level = 0; /* find leaves with maximal level */ while( ptr ) { if( ptr->child ) { ptr = ptr->child; cur_level++; } else { if( cur_level == level ) { last->next_same_level = ptr; ptr->next_same_level = NULL; last = ptr; } if( cur_level > level ) { level = cur_level; leaves = last = ptr; ptr->next_same_level = NULL; } while( ptr && ptr->next == NULL ) { ptr = ptr->parent; cur_level--; } if( ptr ) ptr = ptr->next; } } __END__; return leaves; } /* End of file. */