--- /dev/null
+/*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;
+ fscanf( file, "%d", &nrect );
+
+ assert( nrect <= CV_HAAR_FEATURE_MAX );
+
+ for( j = 0; j < nrect; j++ )
+ {
+ 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 );
+ 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;
+ }
+ fscanf( file, "%s", &(feature->desc[0]) );
+ 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;
+ fscanf( file, "%d", &count );
+ if( count > 0 )
+ {
+ ptr = (CvCARTHaarClassifier*) icvCreateCARTHaarClassifier( count );
+ for( i = 0; i < count; i++ )
+ {
+ icvLoadHaarFeature( &(ptr->feature[i]), file );
+ fscanf( file, "%f %d %d", &(ptr->threshold[i]), &(ptr->left[i]),
+ &(ptr->right[i]) );
+ }
+ for( i = 0; i <= count; i++ )
+ {
+ fscanf( file, "%f", &(ptr->val[i]) );
+ }
+ 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 );
+}
+
+
+
+CvIntHaarClassifier* icvLoadCARTStageHaarClassifierF( FILE* file, int step )
+{
+ CvStageHaarClassifier* ptr = NULL;
+
+ //CV_FUNCNAME( "icvLoadCARTStageHaarClassifierF" );
+
+ __BEGIN__;
+
+ if( file != NULL )
+ {
+ int count;
+ int i;
+ float threshold;
+
+ count = 0;
+ fscanf( file, "%d", &count );
+ if( count > 0 )
+ {
+ ptr = (CvStageHaarClassifier*) icvCreateStageHaarClassifier( count, 0.0F );
+ for( i = 0; i < count; i++ )
+ {
+ ptr->classifier[i] = icvLoadCARTHaarClassifier( file, step );
+ }
+
+ fscanf( file, "%f", &threshold );
+
+ 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. */