3786 lines
116 KiB
C++
3786 lines
116 KiB
C++
/*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*/
|
|
|
|
#ifdef HAVE_CVCONFIG_H
|
|
#include "cvconfig.h"
|
|
#endif
|
|
|
|
#ifdef HAVE_MALLOC_H
|
|
#include <malloc.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_MEMORY_H
|
|
#include <memory.h>
|
|
#endif
|
|
|
|
#ifdef _OPENMP
|
|
#include <omp.h>
|
|
#endif /* _OPENMP */
|
|
|
|
#include <cstdio>
|
|
#include <cfloat>
|
|
#include <cmath>
|
|
#include <ctime>
|
|
#include <climits>
|
|
|
|
#include "_cvcommon.h"
|
|
#include "cvclassifier.h"
|
|
|
|
#ifdef _OPENMP
|
|
#include "omp.h"
|
|
#endif
|
|
|
|
#define CV_BOOST_IMPL
|
|
|
|
typedef struct CvValArray
|
|
{
|
|
uchar* data;
|
|
size_t step;
|
|
} CvValArray;
|
|
|
|
#define CMP_VALUES( idx1, idx2 ) \
|
|
( *( (float*) (aux->data + ((int) (idx1)) * aux->step ) ) < \
|
|
*( (float*) (aux->data + ((int) (idx2)) * aux->step ) ) )
|
|
|
|
static CV_IMPLEMENT_QSORT_EX( icvSortIndexedValArray_16s, short, CMP_VALUES, CvValArray* )
|
|
|
|
static CV_IMPLEMENT_QSORT_EX( icvSortIndexedValArray_32s, int, CMP_VALUES, CvValArray* )
|
|
|
|
static CV_IMPLEMENT_QSORT_EX( icvSortIndexedValArray_32f, float, CMP_VALUES, CvValArray* )
|
|
|
|
CV_BOOST_IMPL
|
|
void cvGetSortedIndices( CvMat* val, CvMat* idx, int sortcols )
|
|
{
|
|
int idxtype = 0;
|
|
size_t istep = 0;
|
|
size_t jstep = 0;
|
|
|
|
int i = 0;
|
|
int j = 0;
|
|
|
|
CvValArray va;
|
|
|
|
CV_Assert( idx != NULL );
|
|
CV_Assert( val != NULL );
|
|
|
|
idxtype = CV_MAT_TYPE( idx->type );
|
|
CV_Assert( idxtype == CV_16SC1 || idxtype == CV_32SC1 || idxtype == CV_32FC1 );
|
|
CV_Assert( CV_MAT_TYPE( val->type ) == CV_32FC1 );
|
|
if( sortcols )
|
|
{
|
|
CV_Assert( idx->rows == val->cols );
|
|
CV_Assert( idx->cols == val->rows );
|
|
istep = CV_ELEM_SIZE( val->type );
|
|
jstep = val->step;
|
|
}
|
|
else
|
|
{
|
|
CV_Assert( idx->rows == val->rows );
|
|
CV_Assert( idx->cols == val->cols );
|
|
istep = val->step;
|
|
jstep = CV_ELEM_SIZE( val->type );
|
|
}
|
|
|
|
va.data = val->data.ptr;
|
|
va.step = jstep;
|
|
switch( idxtype )
|
|
{
|
|
case CV_16SC1:
|
|
for( i = 0; i < idx->rows; i++ )
|
|
{
|
|
for( j = 0; j < idx->cols; j++ )
|
|
{
|
|
CV_MAT_ELEM( *idx, short, i, j ) = (short) j;
|
|
}
|
|
icvSortIndexedValArray_16s( (short*) (idx->data.ptr + (size_t)i * idx->step),
|
|
idx->cols, &va );
|
|
va.data += istep;
|
|
}
|
|
break;
|
|
|
|
case CV_32SC1:
|
|
for( i = 0; i < idx->rows; i++ )
|
|
{
|
|
for( j = 0; j < idx->cols; j++ )
|
|
{
|
|
CV_MAT_ELEM( *idx, int, i, j ) = j;
|
|
}
|
|
icvSortIndexedValArray_32s( (int*) (idx->data.ptr + (size_t)i * idx->step),
|
|
idx->cols, &va );
|
|
va.data += istep;
|
|
}
|
|
break;
|
|
|
|
case CV_32FC1:
|
|
for( i = 0; i < idx->rows; i++ )
|
|
{
|
|
for( j = 0; j < idx->cols; j++ )
|
|
{
|
|
CV_MAT_ELEM( *idx, float, i, j ) = (float) j;
|
|
}
|
|
icvSortIndexedValArray_32f( (float*) (idx->data.ptr + (size_t)i * idx->step),
|
|
idx->cols, &va );
|
|
va.data += istep;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
assert( 0 );
|
|
break;
|
|
}
|
|
}
|
|
|
|
CV_BOOST_IMPL
|
|
void cvReleaseStumpClassifier( CvClassifier** classifier )
|
|
{
|
|
cvFree( classifier );
|
|
*classifier = 0;
|
|
}
|
|
|
|
CV_BOOST_IMPL
|
|
float cvEvalStumpClassifier( CvClassifier* classifier, CvMat* sample )
|
|
{
|
|
assert( classifier != NULL );
|
|
assert( sample != NULL );
|
|
assert( CV_MAT_TYPE( sample->type ) == CV_32FC1 );
|
|
|
|
if( (CV_MAT_ELEM( (*sample), float, 0,
|
|
((CvStumpClassifier*) classifier)->compidx )) <
|
|
((CvStumpClassifier*) classifier)->threshold )
|
|
return ((CvStumpClassifier*) classifier)->left;
|
|
return ((CvStumpClassifier*) classifier)->right;
|
|
}
|
|
|
|
#define ICV_DEF_FIND_STUMP_THRESHOLD( suffix, type, error ) \
|
|
static int icvFindStumpThreshold_##suffix( \
|
|
uchar* data, size_t datastep, \
|
|
uchar* wdata, size_t wstep, \
|
|
uchar* ydata, size_t ystep, \
|
|
uchar* idxdata, size_t idxstep, int num, \
|
|
float* lerror, \
|
|
float* rerror, \
|
|
float* threshold, float* left, float* right, \
|
|
float* sumw, float* sumwy, float* sumwyy ) \
|
|
{ \
|
|
int found = 0; \
|
|
float wyl = 0.0F; \
|
|
float wl = 0.0F; \
|
|
float wyyl = 0.0F; \
|
|
float wyr = 0.0F; \
|
|
float wr = 0.0F; \
|
|
\
|
|
float curleft = 0.0F; \
|
|
float curright = 0.0F; \
|
|
float* prevval = NULL; \
|
|
float* curval = NULL; \
|
|
float curlerror = 0.0F; \
|
|
float currerror = 0.0F; \
|
|
\
|
|
int i = 0; \
|
|
int idx = 0; \
|
|
\
|
|
if( *sumw == FLT_MAX ) \
|
|
{ \
|
|
/* calculate sums */ \
|
|
float *y = NULL; \
|
|
float *w = NULL; \
|
|
float wy = 0.0F; \
|
|
\
|
|
*sumw = 0.0F; \
|
|
*sumwy = 0.0F; \
|
|
*sumwyy = 0.0F; \
|
|
for( i = 0; i < num; i++ ) \
|
|
{ \
|
|
idx = (int) ( *((type*) (idxdata + i*idxstep)) ); \
|
|
w = (float*) (wdata + idx * wstep); \
|
|
*sumw += *w; \
|
|
y = (float*) (ydata + idx * ystep); \
|
|
wy = (*w) * (*y); \
|
|
*sumwy += wy; \
|
|
*sumwyy += wy * (*y); \
|
|
} \
|
|
} \
|
|
\
|
|
for( i = 0; i < num; i++ ) \
|
|
{ \
|
|
idx = (int) ( *((type*) (idxdata + i*idxstep)) ); \
|
|
curval = (float*) (data + idx * datastep); \
|
|
/* for debug purpose */ \
|
|
if( i > 0 ) assert( (*prevval) <= (*curval) ); \
|
|
\
|
|
wyr = *sumwy - wyl; \
|
|
wr = *sumw - wl; \
|
|
\
|
|
if( wl > 0.0 ) curleft = wyl / wl; \
|
|
else curleft = 0.0F; \
|
|
\
|
|
if( wr > 0.0 ) curright = wyr / wr; \
|
|
else curright = 0.0F; \
|
|
\
|
|
error \
|
|
\
|
|
if( curlerror + currerror < (*lerror) + (*rerror) ) \
|
|
{ \
|
|
(*lerror) = curlerror; \
|
|
(*rerror) = currerror; \
|
|
*threshold = *curval; \
|
|
if( i > 0 ) { \
|
|
*threshold = 0.5F * (*threshold + *prevval); \
|
|
} \
|
|
*left = curleft; \
|
|
*right = curright; \
|
|
found = 1; \
|
|
} \
|
|
\
|
|
do \
|
|
{ \
|
|
wl += *((float*) (wdata + idx * wstep)); \
|
|
wyl += (*((float*) (wdata + idx * wstep))) \
|
|
* (*((float*) (ydata + idx * ystep))); \
|
|
wyyl += *((float*) (wdata + idx * wstep)) \
|
|
* (*((float*) (ydata + idx * ystep))) \
|
|
* (*((float*) (ydata + idx * ystep))); \
|
|
} \
|
|
while( (++i) < num && \
|
|
( *((float*) (data + (idx = \
|
|
(int) ( *((type*) (idxdata + i*idxstep))) ) * datastep)) \
|
|
== *curval ) ); \
|
|
--i; \
|
|
prevval = curval; \
|
|
} /* for each value */ \
|
|
\
|
|
return found; \
|
|
}
|
|
|
|
/* misclassification error
|
|
* err = MIN( wpos, wneg );
|
|
*/
|
|
#define ICV_DEF_FIND_STUMP_THRESHOLD_MISC( suffix, type ) \
|
|
ICV_DEF_FIND_STUMP_THRESHOLD( misc_##suffix, type, \
|
|
float wposl = 0.5F * ( wl + wyl ); \
|
|
float wposr = 0.5F * ( wr + wyr ); \
|
|
curleft = 0.5F * ( 1.0F + curleft ); \
|
|
curright = 0.5F * ( 1.0F + curright ); \
|
|
curlerror = MIN( wposl, wl - wposl ); \
|
|
currerror = MIN( wposr, wr - wposr ); \
|
|
)
|
|
|
|
/* gini error
|
|
* err = 2 * wpos * wneg /(wpos + wneg)
|
|
*/
|
|
#define ICV_DEF_FIND_STUMP_THRESHOLD_GINI( suffix, type ) \
|
|
ICV_DEF_FIND_STUMP_THRESHOLD( gini_##suffix, type, \
|
|
float wposl = 0.5F * ( wl + wyl ); \
|
|
float wposr = 0.5F * ( wr + wyr ); \
|
|
curleft = 0.5F * ( 1.0F + curleft ); \
|
|
curright = 0.5F * ( 1.0F + curright ); \
|
|
curlerror = 2.0F * wposl * ( 1.0F - curleft ); \
|
|
currerror = 2.0F * wposr * ( 1.0F - curright ); \
|
|
)
|
|
|
|
#define CV_ENTROPY_THRESHOLD FLT_MIN
|
|
|
|
/* entropy error
|
|
* err = - wpos * log(wpos / (wpos + wneg)) - wneg * log(wneg / (wpos + wneg))
|
|
*/
|
|
#define ICV_DEF_FIND_STUMP_THRESHOLD_ENTROPY( suffix, type ) \
|
|
ICV_DEF_FIND_STUMP_THRESHOLD( entropy_##suffix, type, \
|
|
float wposl = 0.5F * ( wl + wyl ); \
|
|
float wposr = 0.5F * ( wr + wyr ); \
|
|
curleft = 0.5F * ( 1.0F + curleft ); \
|
|
curright = 0.5F * ( 1.0F + curright ); \
|
|
curlerror = currerror = 0.0F; \
|
|
if( curleft > CV_ENTROPY_THRESHOLD ) \
|
|
curlerror -= wposl * logf( curleft ); \
|
|
if( curleft < 1.0F - CV_ENTROPY_THRESHOLD ) \
|
|
curlerror -= (wl - wposl) * logf( 1.0F - curleft ); \
|
|
\
|
|
if( curright > CV_ENTROPY_THRESHOLD ) \
|
|
currerror -= wposr * logf( curright ); \
|
|
if( curright < 1.0F - CV_ENTROPY_THRESHOLD ) \
|
|
currerror -= (wr - wposr) * logf( 1.0F - curright ); \
|
|
)
|
|
|
|
/* least sum of squares error */
|
|
#define ICV_DEF_FIND_STUMP_THRESHOLD_SQ( suffix, type ) \
|
|
ICV_DEF_FIND_STUMP_THRESHOLD( sq_##suffix, type, \
|
|
/* calculate error (sum of squares) */ \
|
|
/* err = sum( w * (y - left(rigt)Val)^2 ) */ \
|
|
curlerror = wyyl + curleft * curleft * wl - 2.0F * curleft * wyl; \
|
|
currerror = (*sumwyy) - wyyl + curright * curright * wr - 2.0F * curright * wyr; \
|
|
)
|
|
|
|
ICV_DEF_FIND_STUMP_THRESHOLD_MISC( 16s, short )
|
|
|
|
ICV_DEF_FIND_STUMP_THRESHOLD_MISC( 32s, int )
|
|
|
|
ICV_DEF_FIND_STUMP_THRESHOLD_MISC( 32f, float )
|
|
|
|
|
|
ICV_DEF_FIND_STUMP_THRESHOLD_GINI( 16s, short )
|
|
|
|
ICV_DEF_FIND_STUMP_THRESHOLD_GINI( 32s, int )
|
|
|
|
ICV_DEF_FIND_STUMP_THRESHOLD_GINI( 32f, float )
|
|
|
|
|
|
ICV_DEF_FIND_STUMP_THRESHOLD_ENTROPY( 16s, short )
|
|
|
|
ICV_DEF_FIND_STUMP_THRESHOLD_ENTROPY( 32s, int )
|
|
|
|
ICV_DEF_FIND_STUMP_THRESHOLD_ENTROPY( 32f, float )
|
|
|
|
|
|
ICV_DEF_FIND_STUMP_THRESHOLD_SQ( 16s, short )
|
|
|
|
ICV_DEF_FIND_STUMP_THRESHOLD_SQ( 32s, int )
|
|
|
|
ICV_DEF_FIND_STUMP_THRESHOLD_SQ( 32f, float )
|
|
|
|
typedef int (*CvFindThresholdFunc)( uchar* data, size_t datastep,
|
|
uchar* wdata, size_t wstep,
|
|
uchar* ydata, size_t ystep,
|
|
uchar* idxdata, size_t idxstep, int num,
|
|
float* lerror,
|
|
float* rerror,
|
|
float* threshold, float* left, float* right,
|
|
float* sumw, float* sumwy, float* sumwyy );
|
|
|
|
CvFindThresholdFunc findStumpThreshold_16s[4] = {
|
|
icvFindStumpThreshold_misc_16s,
|
|
icvFindStumpThreshold_gini_16s,
|
|
icvFindStumpThreshold_entropy_16s,
|
|
icvFindStumpThreshold_sq_16s
|
|
};
|
|
|
|
CvFindThresholdFunc findStumpThreshold_32s[4] = {
|
|
icvFindStumpThreshold_misc_32s,
|
|
icvFindStumpThreshold_gini_32s,
|
|
icvFindStumpThreshold_entropy_32s,
|
|
icvFindStumpThreshold_sq_32s
|
|
};
|
|
|
|
CvFindThresholdFunc findStumpThreshold_32f[4] = {
|
|
icvFindStumpThreshold_misc_32f,
|
|
icvFindStumpThreshold_gini_32f,
|
|
icvFindStumpThreshold_entropy_32f,
|
|
icvFindStumpThreshold_sq_32f
|
|
};
|
|
|
|
CV_BOOST_IMPL
|
|
CvClassifier* cvCreateStumpClassifier( CvMat* trainData,
|
|
int flags,
|
|
CvMat* trainClasses,
|
|
CvMat* /*typeMask*/,
|
|
CvMat* missedMeasurementsMask,
|
|
CvMat* compIdx,
|
|
CvMat* sampleIdx,
|
|
CvMat* weights,
|
|
CvClassifierTrainParams* trainParams
|
|
)
|
|
{
|
|
CvStumpClassifier* stump = NULL;
|
|
int m = 0; /* number of samples */
|
|
int n = 0; /* number of components */
|
|
uchar* data = NULL;
|
|
int cstep = 0;
|
|
int sstep = 0;
|
|
uchar* ydata = NULL;
|
|
int ystep = 0;
|
|
uchar* idxdata = NULL;
|
|
int idxstep = 0;
|
|
int l = 0; /* number of indices */
|
|
uchar* wdata = NULL;
|
|
int wstep = 0;
|
|
|
|
int* idx = NULL;
|
|
int i = 0;
|
|
|
|
float sumw = FLT_MAX;
|
|
float sumwy = FLT_MAX;
|
|
float sumwyy = FLT_MAX;
|
|
|
|
CV_Assert( trainData != NULL );
|
|
CV_Assert( CV_MAT_TYPE( trainData->type ) == CV_32FC1 );
|
|
CV_Assert( trainClasses != NULL );
|
|
CV_Assert( CV_MAT_TYPE( trainClasses->type ) == CV_32FC1 );
|
|
CV_Assert( missedMeasurementsMask == NULL );
|
|
CV_Assert( compIdx == NULL );
|
|
CV_Assert( weights != NULL );
|
|
CV_Assert( CV_MAT_TYPE( weights->type ) == CV_32FC1 );
|
|
CV_Assert( trainParams != NULL );
|
|
|
|
data = trainData->data.ptr;
|
|
if( CV_IS_ROW_SAMPLE( flags ) )
|
|
{
|
|
cstep = CV_ELEM_SIZE( trainData->type );
|
|
sstep = trainData->step;
|
|
m = trainData->rows;
|
|
n = trainData->cols;
|
|
}
|
|
else
|
|
{
|
|
sstep = CV_ELEM_SIZE( trainData->type );
|
|
cstep = trainData->step;
|
|
m = trainData->cols;
|
|
n = trainData->rows;
|
|
}
|
|
|
|
ydata = trainClasses->data.ptr;
|
|
if( trainClasses->rows == 1 )
|
|
{
|
|
assert( trainClasses->cols == m );
|
|
ystep = CV_ELEM_SIZE( trainClasses->type );
|
|
}
|
|
else
|
|
{
|
|
assert( trainClasses->rows == m );
|
|
ystep = trainClasses->step;
|
|
}
|
|
|
|
wdata = weights->data.ptr;
|
|
if( weights->rows == 1 )
|
|
{
|
|
assert( weights->cols == m );
|
|
wstep = CV_ELEM_SIZE( weights->type );
|
|
}
|
|
else
|
|
{
|
|
assert( weights->rows == m );
|
|
wstep = weights->step;
|
|
}
|
|
|
|
l = m;
|
|
if( sampleIdx != NULL )
|
|
{
|
|
assert( CV_MAT_TYPE( sampleIdx->type ) == CV_32FC1 );
|
|
|
|
idxdata = sampleIdx->data.ptr;
|
|
if( sampleIdx->rows == 1 )
|
|
{
|
|
l = sampleIdx->cols;
|
|
idxstep = CV_ELEM_SIZE( sampleIdx->type );
|
|
}
|
|
else
|
|
{
|
|
l = sampleIdx->rows;
|
|
idxstep = sampleIdx->step;
|
|
}
|
|
assert( l <= m );
|
|
}
|
|
|
|
idx = (int*) cvAlloc( l * sizeof( int ) );
|
|
stump = (CvStumpClassifier*) cvAlloc( sizeof( CvStumpClassifier) );
|
|
|
|
/* START */
|
|
memset( (void*) stump, 0, sizeof( CvStumpClassifier ) );
|
|
|
|
stump->eval = cvEvalStumpClassifier;
|
|
stump->tune = NULL;
|
|
stump->save = NULL;
|
|
stump->release = cvReleaseStumpClassifier;
|
|
|
|
stump->lerror = FLT_MAX;
|
|
stump->rerror = FLT_MAX;
|
|
stump->left = 0.0F;
|
|
stump->right = 0.0F;
|
|
|
|
/* copy indices */
|
|
if( sampleIdx != NULL )
|
|
{
|
|
for( i = 0; i < l; i++ )
|
|
{
|
|
idx[i] = (int) *((float*) (idxdata + i*idxstep));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for( i = 0; i < l; i++ )
|
|
{
|
|
idx[i] = i;
|
|
}
|
|
}
|
|
|
|
for( i = 0; i < n; i++ )
|
|
{
|
|
CvValArray va;
|
|
|
|
va.data = data + i * ((size_t) cstep);
|
|
va.step = sstep;
|
|
icvSortIndexedValArray_32s( idx, l, &va );
|
|
if( findStumpThreshold_32s[(int) ((CvStumpTrainParams*) trainParams)->error]
|
|
( data + i * ((size_t) cstep), sstep,
|
|
wdata, wstep, ydata, ystep, (uchar*) idx, sizeof( int ), l,
|
|
&(stump->lerror), &(stump->rerror),
|
|
&(stump->threshold), &(stump->left), &(stump->right),
|
|
&sumw, &sumwy, &sumwyy ) )
|
|
{
|
|
stump->compidx = i;
|
|
}
|
|
} /* for each component */
|
|
|
|
/* END */
|
|
|
|
cvFree( &idx );
|
|
|
|
if( ((CvStumpTrainParams*) trainParams)->type == CV_CLASSIFICATION_CLASS )
|
|
{
|
|
stump->left = 2.0F * (stump->left >= 0.5F) - 1.0F;
|
|
stump->right = 2.0F * (stump->right >= 0.5F) - 1.0F;
|
|
}
|
|
|
|
return (CvClassifier*) stump;
|
|
}
|
|
|
|
/*
|
|
* cvCreateMTStumpClassifier
|
|
*
|
|
* Multithreaded stump classifier constructor
|
|
* Includes huge train data support through callback function
|
|
*/
|
|
CV_BOOST_IMPL
|
|
CvClassifier* cvCreateMTStumpClassifier( CvMat* trainData,
|
|
int flags,
|
|
CvMat* trainClasses,
|
|
CvMat* /*typeMask*/,
|
|
CvMat* missedMeasurementsMask,
|
|
CvMat* compIdx,
|
|
CvMat* sampleIdx,
|
|
CvMat* weights,
|
|
CvClassifierTrainParams* trainParams )
|
|
{
|
|
CvStumpClassifier* stump = NULL;
|
|
int m = 0; /* number of samples */
|
|
int n = 0; /* number of components */
|
|
uchar* data = NULL;
|
|
size_t cstep = 0;
|
|
size_t sstep = 0;
|
|
int datan = 0; /* num components */
|
|
uchar* ydata = NULL;
|
|
size_t ystep = 0;
|
|
uchar* idxdata = NULL;
|
|
size_t idxstep = 0;
|
|
int l = 0; /* number of indices */
|
|
uchar* wdata = NULL;
|
|
size_t wstep = 0;
|
|
|
|
uchar* sorteddata = NULL;
|
|
int sortedtype = 0;
|
|
size_t sortedcstep = 0; /* component step */
|
|
size_t sortedsstep = 0; /* sample step */
|
|
int sortedn = 0; /* num components */
|
|
int sortedm = 0; /* num samples */
|
|
|
|
char* filter = NULL;
|
|
int i = 0;
|
|
|
|
int compidx = 0;
|
|
int stumperror;
|
|
int portion;
|
|
|
|
/* private variables */
|
|
CvMat mat;
|
|
CvValArray va;
|
|
float lerror;
|
|
float rerror;
|
|
float left;
|
|
float right;
|
|
float threshold;
|
|
int optcompidx;
|
|
|
|
float sumw;
|
|
float sumwy;
|
|
float sumwyy;
|
|
|
|
int t_compidx;
|
|
int t_n;
|
|
|
|
int ti;
|
|
int tj;
|
|
int tk;
|
|
|
|
uchar* t_data;
|
|
size_t t_cstep;
|
|
size_t t_sstep;
|
|
|
|
size_t matcstep;
|
|
size_t matsstep;
|
|
|
|
int* t_idx;
|
|
/* end private variables */
|
|
|
|
CV_Assert( trainParams != NULL );
|
|
CV_Assert( trainClasses != NULL );
|
|
CV_Assert( CV_MAT_TYPE( trainClasses->type ) == CV_32FC1 );
|
|
CV_Assert( missedMeasurementsMask == NULL );
|
|
CV_Assert( compIdx == NULL );
|
|
|
|
stumperror = (int) ((CvMTStumpTrainParams*) trainParams)->error;
|
|
|
|
ydata = trainClasses->data.ptr;
|
|
if( trainClasses->rows == 1 )
|
|
{
|
|
m = trainClasses->cols;
|
|
ystep = CV_ELEM_SIZE( trainClasses->type );
|
|
}
|
|
else
|
|
{
|
|
m = trainClasses->rows;
|
|
ystep = trainClasses->step;
|
|
}
|
|
|
|
wdata = weights->data.ptr;
|
|
if( weights->rows == 1 )
|
|
{
|
|
CV_Assert( weights->cols == m );
|
|
wstep = CV_ELEM_SIZE( weights->type );
|
|
}
|
|
else
|
|
{
|
|
CV_Assert( weights->rows == m );
|
|
wstep = weights->step;
|
|
}
|
|
|
|
if( ((CvMTStumpTrainParams*) trainParams)->sortedIdx != NULL )
|
|
{
|
|
sortedtype =
|
|
CV_MAT_TYPE( ((CvMTStumpTrainParams*) trainParams)->sortedIdx->type );
|
|
assert( sortedtype == CV_16SC1 || sortedtype == CV_32SC1
|
|
|| sortedtype == CV_32FC1 );
|
|
sorteddata = ((CvMTStumpTrainParams*) trainParams)->sortedIdx->data.ptr;
|
|
sortedsstep = CV_ELEM_SIZE( sortedtype );
|
|
sortedcstep = ((CvMTStumpTrainParams*) trainParams)->sortedIdx->step;
|
|
sortedn = ((CvMTStumpTrainParams*) trainParams)->sortedIdx->rows;
|
|
sortedm = ((CvMTStumpTrainParams*) trainParams)->sortedIdx->cols;
|
|
}
|
|
|
|
if( trainData == NULL )
|
|
{
|
|
assert( ((CvMTStumpTrainParams*) trainParams)->getTrainData != NULL );
|
|
n = ((CvMTStumpTrainParams*) trainParams)->numcomp;
|
|
assert( n > 0 );
|
|
}
|
|
else
|
|
{
|
|
assert( CV_MAT_TYPE( trainData->type ) == CV_32FC1 );
|
|
data = trainData->data.ptr;
|
|
if( CV_IS_ROW_SAMPLE( flags ) )
|
|
{
|
|
cstep = CV_ELEM_SIZE( trainData->type );
|
|
sstep = trainData->step;
|
|
assert( m == trainData->rows );
|
|
datan = n = trainData->cols;
|
|
}
|
|
else
|
|
{
|
|
sstep = CV_ELEM_SIZE( trainData->type );
|
|
cstep = trainData->step;
|
|
assert( m == trainData->cols );
|
|
datan = n = trainData->rows;
|
|
}
|
|
if( ((CvMTStumpTrainParams*) trainParams)->getTrainData != NULL )
|
|
{
|
|
n = ((CvMTStumpTrainParams*) trainParams)->numcomp;
|
|
}
|
|
}
|
|
assert( datan <= n );
|
|
|
|
if( sampleIdx != NULL )
|
|
{
|
|
assert( CV_MAT_TYPE( sampleIdx->type ) == CV_32FC1 );
|
|
idxdata = sampleIdx->data.ptr;
|
|
idxstep = ( sampleIdx->rows == 1 )
|
|
? CV_ELEM_SIZE( sampleIdx->type ) : sampleIdx->step;
|
|
l = ( sampleIdx->rows == 1 ) ? sampleIdx->cols : sampleIdx->rows;
|
|
|
|
if( sorteddata != NULL )
|
|
{
|
|
filter = (char*) cvAlloc( sizeof( char ) * m );
|
|
memset( (void*) filter, 0, sizeof( char ) * m );
|
|
for( i = 0; i < l; i++ )
|
|
{
|
|
filter[(int) *((float*) (idxdata + i * idxstep))] = (char) 1;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
l = m;
|
|
}
|
|
|
|
stump = (CvStumpClassifier*) cvAlloc( sizeof( CvStumpClassifier) );
|
|
|
|
/* START */
|
|
memset( (void*) stump, 0, sizeof( CvStumpClassifier ) );
|
|
|
|
portion = ((CvMTStumpTrainParams*)trainParams)->portion;
|
|
|
|
if( portion < 1 )
|
|
{
|
|
/* auto portion */
|
|
portion = n;
|
|
#ifdef _OPENMP
|
|
portion /= omp_get_max_threads();
|
|
#endif /* _OPENMP */
|
|
}
|
|
|
|
stump->eval = cvEvalStumpClassifier;
|
|
stump->tune = NULL;
|
|
stump->save = NULL;
|
|
stump->release = cvReleaseStumpClassifier;
|
|
|
|
stump->lerror = FLT_MAX;
|
|
stump->rerror = FLT_MAX;
|
|
stump->left = 0.0F;
|
|
stump->right = 0.0F;
|
|
|
|
compidx = 0;
|
|
#ifdef _OPENMP
|
|
#pragma omp parallel private(mat, va, lerror, rerror, left, right, threshold, \
|
|
optcompidx, sumw, sumwy, sumwyy, t_compidx, t_n, \
|
|
ti, tj, tk, t_data, t_cstep, t_sstep, matcstep, \
|
|
matsstep, t_idx)
|
|
#endif /* _OPENMP */
|
|
{
|
|
lerror = FLT_MAX;
|
|
rerror = FLT_MAX;
|
|
left = 0.0F;
|
|
right = 0.0F;
|
|
threshold = 0.0F;
|
|
optcompidx = 0;
|
|
|
|
sumw = FLT_MAX;
|
|
sumwy = FLT_MAX;
|
|
sumwyy = FLT_MAX;
|
|
|
|
t_compidx = 0;
|
|
t_n = 0;
|
|
|
|
ti = 0;
|
|
tj = 0;
|
|
tk = 0;
|
|
|
|
t_data = NULL;
|
|
t_cstep = 0;
|
|
t_sstep = 0;
|
|
|
|
matcstep = 0;
|
|
matsstep = 0;
|
|
|
|
t_idx = NULL;
|
|
|
|
mat.data.ptr = NULL;
|
|
|
|
if( datan < n )
|
|
{
|
|
/* prepare matrix for callback */
|
|
if( CV_IS_ROW_SAMPLE( flags ) )
|
|
{
|
|
mat = cvMat( m, portion, CV_32FC1, 0 );
|
|
matcstep = CV_ELEM_SIZE( mat.type );
|
|
matsstep = mat.step;
|
|
}
|
|
else
|
|
{
|
|
mat = cvMat( portion, m, CV_32FC1, 0 );
|
|
matcstep = mat.step;
|
|
matsstep = CV_ELEM_SIZE( mat.type );
|
|
}
|
|
mat.data.ptr = (uchar*) cvAlloc( sizeof( float ) * mat.rows * mat.cols );
|
|
}
|
|
|
|
if( filter != NULL || sortedn < n )
|
|
{
|
|
t_idx = (int*) cvAlloc( sizeof( int ) * m );
|
|
if( sortedn == 0 || filter == NULL )
|
|
{
|
|
if( idxdata != NULL )
|
|
{
|
|
for( ti = 0; ti < l; ti++ )
|
|
{
|
|
t_idx[ti] = (int) *((float*) (idxdata + ti * idxstep));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for( ti = 0; ti < l; ti++ )
|
|
{
|
|
t_idx[ti] = ti;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef _OPENMP
|
|
#pragma omp critical(c_compidx)
|
|
#endif /* _OPENMP */
|
|
{
|
|
t_compidx = compidx;
|
|
compidx += portion;
|
|
}
|
|
while( t_compidx < n )
|
|
{
|
|
t_n = portion;
|
|
if( t_compidx < datan )
|
|
{
|
|
t_n = ( t_n < (datan - t_compidx) ) ? t_n : (datan - t_compidx);
|
|
t_data = data;
|
|
t_cstep = cstep;
|
|
t_sstep = sstep;
|
|
}
|
|
else
|
|
{
|
|
t_n = ( t_n < (n - t_compidx) ) ? t_n : (n - t_compidx);
|
|
t_cstep = matcstep;
|
|
t_sstep = matsstep;
|
|
t_data = mat.data.ptr - t_compidx * ((size_t) t_cstep );
|
|
|
|
/* calculate components */
|
|
((CvMTStumpTrainParams*)trainParams)->getTrainData( &mat,
|
|
sampleIdx, compIdx, t_compidx, t_n,
|
|
((CvMTStumpTrainParams*)trainParams)->userdata );
|
|
}
|
|
|
|
if( sorteddata != NULL )
|
|
{
|
|
if( filter != NULL )
|
|
{
|
|
/* have sorted indices and filter */
|
|
switch( sortedtype )
|
|
{
|
|
case CV_16SC1:
|
|
for( ti = t_compidx; ti < MIN( sortedn, t_compidx + t_n ); ti++ )
|
|
{
|
|
tk = 0;
|
|
for( tj = 0; tj < sortedm; tj++ )
|
|
{
|
|
int curidx = (int) ( *((short*) (sorteddata
|
|
+ ti * sortedcstep + tj * sortedsstep)) );
|
|
if( filter[curidx] != 0 )
|
|
{
|
|
t_idx[tk++] = curidx;
|
|
}
|
|
}
|
|
if( findStumpThreshold_32s[stumperror](
|
|
t_data + ti * t_cstep, t_sstep,
|
|
wdata, wstep, ydata, ystep,
|
|
(uchar*) t_idx, sizeof( int ), tk,
|
|
&lerror, &rerror,
|
|
&threshold, &left, &right,
|
|
&sumw, &sumwy, &sumwyy ) )
|
|
{
|
|
optcompidx = ti;
|
|
}
|
|
}
|
|
break;
|
|
case CV_32SC1:
|
|
for( ti = t_compidx; ti < MIN( sortedn, t_compidx + t_n ); ti++ )
|
|
{
|
|
tk = 0;
|
|
for( tj = 0; tj < sortedm; tj++ )
|
|
{
|
|
int curidx = (int) ( *((int*) (sorteddata
|
|
+ ti * sortedcstep + tj * sortedsstep)) );
|
|
if( filter[curidx] != 0 )
|
|
{
|
|
t_idx[tk++] = curidx;
|
|
}
|
|
}
|
|
if( findStumpThreshold_32s[stumperror](
|
|
t_data + ti * t_cstep, t_sstep,
|
|
wdata, wstep, ydata, ystep,
|
|
(uchar*) t_idx, sizeof( int ), tk,
|
|
&lerror, &rerror,
|
|
&threshold, &left, &right,
|
|
&sumw, &sumwy, &sumwyy ) )
|
|
{
|
|
optcompidx = ti;
|
|
}
|
|
}
|
|
break;
|
|
case CV_32FC1:
|
|
for( ti = t_compidx; ti < MIN( sortedn, t_compidx + t_n ); ti++ )
|
|
{
|
|
tk = 0;
|
|
for( tj = 0; tj < sortedm; tj++ )
|
|
{
|
|
int curidx = (int) ( *((float*) (sorteddata
|
|
+ ti * sortedcstep + tj * sortedsstep)) );
|
|
if( filter[curidx] != 0 )
|
|
{
|
|
t_idx[tk++] = curidx;
|
|
}
|
|
}
|
|
if( findStumpThreshold_32s[stumperror](
|
|
t_data + ti * t_cstep, t_sstep,
|
|
wdata, wstep, ydata, ystep,
|
|
(uchar*) t_idx, sizeof( int ), tk,
|
|
&lerror, &rerror,
|
|
&threshold, &left, &right,
|
|
&sumw, &sumwy, &sumwyy ) )
|
|
{
|
|
optcompidx = ti;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
assert( 0 );
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* have sorted indices */
|
|
switch( sortedtype )
|
|
{
|
|
case CV_16SC1:
|
|
for( ti = t_compidx; ti < MIN( sortedn, t_compidx + t_n ); ti++ )
|
|
{
|
|
if( findStumpThreshold_16s[stumperror](
|
|
t_data + ti * t_cstep, t_sstep,
|
|
wdata, wstep, ydata, ystep,
|
|
sorteddata + ti * sortedcstep, sortedsstep, sortedm,
|
|
&lerror, &rerror,
|
|
&threshold, &left, &right,
|
|
&sumw, &sumwy, &sumwyy ) )
|
|
{
|
|
optcompidx = ti;
|
|
}
|
|
}
|
|
break;
|
|
case CV_32SC1:
|
|
for( ti = t_compidx; ti < MIN( sortedn, t_compidx + t_n ); ti++ )
|
|
{
|
|
if( findStumpThreshold_32s[stumperror](
|
|
t_data + ti * t_cstep, t_sstep,
|
|
wdata, wstep, ydata, ystep,
|
|
sorteddata + ti * sortedcstep, sortedsstep, sortedm,
|
|
&lerror, &rerror,
|
|
&threshold, &left, &right,
|
|
&sumw, &sumwy, &sumwyy ) )
|
|
{
|
|
optcompidx = ti;
|
|
}
|
|
}
|
|
break;
|
|
case CV_32FC1:
|
|
for( ti = t_compidx; ti < MIN( sortedn, t_compidx + t_n ); ti++ )
|
|
{
|
|
if( findStumpThreshold_32f[stumperror](
|
|
t_data + ti * t_cstep, t_sstep,
|
|
wdata, wstep, ydata, ystep,
|
|
sorteddata + ti * sortedcstep, sortedsstep, sortedm,
|
|
&lerror, &rerror,
|
|
&threshold, &left, &right,
|
|
&sumw, &sumwy, &sumwyy ) )
|
|
{
|
|
optcompidx = ti;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
assert( 0 );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
ti = MAX( t_compidx, MIN( sortedn, t_compidx + t_n ) );
|
|
for( ; ti < t_compidx + t_n; ti++ )
|
|
{
|
|
va.data = t_data + ti * t_cstep;
|
|
va.step = t_sstep;
|
|
icvSortIndexedValArray_32s( t_idx, l, &va );
|
|
if( findStumpThreshold_32s[stumperror](
|
|
t_data + ti * t_cstep, t_sstep,
|
|
wdata, wstep, ydata, ystep,
|
|
(uchar*)t_idx, sizeof( int ), l,
|
|
&lerror, &rerror,
|
|
&threshold, &left, &right,
|
|
&sumw, &sumwy, &sumwyy ) )
|
|
{
|
|
optcompidx = ti;
|
|
}
|
|
}
|
|
#ifdef _OPENMP
|
|
#pragma omp critical(c_compidx)
|
|
#endif /* _OPENMP */
|
|
{
|
|
t_compidx = compidx;
|
|
compidx += portion;
|
|
}
|
|
} /* while have training data */
|
|
|
|
/* get the best classifier */
|
|
#ifdef _OPENMP
|
|
#pragma omp critical(c_beststump)
|
|
#endif /* _OPENMP */
|
|
{
|
|
if( lerror + rerror < stump->lerror + stump->rerror )
|
|
{
|
|
stump->lerror = lerror;
|
|
stump->rerror = rerror;
|
|
stump->compidx = optcompidx;
|
|
stump->threshold = threshold;
|
|
stump->left = left;
|
|
stump->right = right;
|
|
}
|
|
}
|
|
|
|
/* free allocated memory */
|
|
if( mat.data.ptr != NULL )
|
|
{
|
|
cvFree( &(mat.data.ptr) );
|
|
}
|
|
if( t_idx != NULL )
|
|
{
|
|
cvFree( &t_idx );
|
|
}
|
|
} /* end of parallel region */
|
|
|
|
/* END */
|
|
|
|
/* free allocated memory */
|
|
if( filter != NULL )
|
|
{
|
|
cvFree( &filter );
|
|
}
|
|
|
|
if( ((CvMTStumpTrainParams*) trainParams)->type == CV_CLASSIFICATION_CLASS )
|
|
{
|
|
stump->left = 2.0F * (stump->left >= 0.5F) - 1.0F;
|
|
stump->right = 2.0F * (stump->right >= 0.5F) - 1.0F;
|
|
}
|
|
|
|
return (CvClassifier*) stump;
|
|
}
|
|
|
|
CV_BOOST_IMPL
|
|
float cvEvalCARTClassifier( CvClassifier* classifier, CvMat* sample )
|
|
{
|
|
CV_FUNCNAME( "cvEvalCARTClassifier" );
|
|
|
|
int idx = 0;
|
|
|
|
__BEGIN__;
|
|
|
|
|
|
CV_ASSERT( classifier != NULL );
|
|
CV_ASSERT( sample != NULL );
|
|
CV_ASSERT( CV_MAT_TYPE( sample->type ) == CV_32FC1 );
|
|
CV_ASSERT( sample->rows == 1 || sample->cols == 1 );
|
|
|
|
if( sample->rows == 1 )
|
|
{
|
|
do
|
|
{
|
|
if( (CV_MAT_ELEM( (*sample), float, 0,
|
|
((CvCARTClassifier*) classifier)->compidx[idx] )) <
|
|
((CvCARTClassifier*) classifier)->threshold[idx] )
|
|
{
|
|
idx = ((CvCARTClassifier*) classifier)->left[idx];
|
|
}
|
|
else
|
|
{
|
|
idx = ((CvCARTClassifier*) classifier)->right[idx];
|
|
}
|
|
} while( idx > 0 );
|
|
}
|
|
else
|
|
{
|
|
do
|
|
{
|
|
if( (CV_MAT_ELEM( (*sample), float,
|
|
((CvCARTClassifier*) classifier)->compidx[idx], 0 )) <
|
|
((CvCARTClassifier*) classifier)->threshold[idx] )
|
|
{
|
|
idx = ((CvCARTClassifier*) classifier)->left[idx];
|
|
}
|
|
else
|
|
{
|
|
idx = ((CvCARTClassifier*) classifier)->right[idx];
|
|
}
|
|
} while( idx > 0 );
|
|
}
|
|
|
|
__END__;
|
|
|
|
return ((CvCARTClassifier*) classifier)->val[-idx];
|
|
}
|
|
|
|
static
|
|
float cvEvalCARTClassifierIdx( CvClassifier* classifier, CvMat* sample )
|
|
{
|
|
CV_FUNCNAME( "cvEvalCARTClassifierIdx" );
|
|
|
|
int idx = 0;
|
|
|
|
__BEGIN__;
|
|
|
|
|
|
CV_ASSERT( classifier != NULL );
|
|
CV_ASSERT( sample != NULL );
|
|
CV_ASSERT( CV_MAT_TYPE( sample->type ) == CV_32FC1 );
|
|
CV_ASSERT( sample->rows == 1 || sample->cols == 1 );
|
|
|
|
if( sample->rows == 1 )
|
|
{
|
|
do
|
|
{
|
|
if( (CV_MAT_ELEM( (*sample), float, 0,
|
|
((CvCARTClassifier*) classifier)->compidx[idx] )) <
|
|
((CvCARTClassifier*) classifier)->threshold[idx] )
|
|
{
|
|
idx = ((CvCARTClassifier*) classifier)->left[idx];
|
|
}
|
|
else
|
|
{
|
|
idx = ((CvCARTClassifier*) classifier)->right[idx];
|
|
}
|
|
} while( idx > 0 );
|
|
}
|
|
else
|
|
{
|
|
do
|
|
{
|
|
if( (CV_MAT_ELEM( (*sample), float,
|
|
((CvCARTClassifier*) classifier)->compidx[idx], 0 )) <
|
|
((CvCARTClassifier*) classifier)->threshold[idx] )
|
|
{
|
|
idx = ((CvCARTClassifier*) classifier)->left[idx];
|
|
}
|
|
else
|
|
{
|
|
idx = ((CvCARTClassifier*) classifier)->right[idx];
|
|
}
|
|
} while( idx > 0 );
|
|
}
|
|
|
|
__END__;
|
|
|
|
return (float) (-idx);
|
|
}
|
|
|
|
CV_BOOST_IMPL
|
|
void cvReleaseCARTClassifier( CvClassifier** classifier )
|
|
{
|
|
cvFree( classifier );
|
|
*classifier = NULL;
|
|
}
|
|
|
|
static void CV_CDECL icvDefaultSplitIdx_R( int compidx, float threshold,
|
|
CvMat* idx, CvMat** left, CvMat** right,
|
|
void* userdata )
|
|
{
|
|
CvMat* trainData = (CvMat*) userdata;
|
|
int i = 0;
|
|
|
|
*left = cvCreateMat( 1, trainData->rows, CV_32FC1 );
|
|
*right = cvCreateMat( 1, trainData->rows, CV_32FC1 );
|
|
(*left)->cols = (*right)->cols = 0;
|
|
if( idx == NULL )
|
|
{
|
|
for( i = 0; i < trainData->rows; i++ )
|
|
{
|
|
if( CV_MAT_ELEM( *trainData, float, i, compidx ) < threshold )
|
|
{
|
|
(*left)->data.fl[(*left)->cols++] = (float) i;
|
|
}
|
|
else
|
|
{
|
|
(*right)->data.fl[(*right)->cols++] = (float) i;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
uchar* idxdata;
|
|
int idxnum;
|
|
int idxstep;
|
|
int index;
|
|
|
|
idxdata = idx->data.ptr;
|
|
idxnum = (idx->rows == 1) ? idx->cols : idx->rows;
|
|
idxstep = (idx->rows == 1) ? CV_ELEM_SIZE( idx->type ) : idx->step;
|
|
for( i = 0; i < idxnum; i++ )
|
|
{
|
|
index = (int) *((float*) (idxdata + i * idxstep));
|
|
if( CV_MAT_ELEM( *trainData, float, index, compidx ) < threshold )
|
|
{
|
|
(*left)->data.fl[(*left)->cols++] = (float) index;
|
|
}
|
|
else
|
|
{
|
|
(*right)->data.fl[(*right)->cols++] = (float) index;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void CV_CDECL icvDefaultSplitIdx_C( int compidx, float threshold,
|
|
CvMat* idx, CvMat** left, CvMat** right,
|
|
void* userdata )
|
|
{
|
|
CvMat* trainData = (CvMat*) userdata;
|
|
int i = 0;
|
|
|
|
*left = cvCreateMat( 1, trainData->cols, CV_32FC1 );
|
|
*right = cvCreateMat( 1, trainData->cols, CV_32FC1 );
|
|
(*left)->cols = (*right)->cols = 0;
|
|
if( idx == NULL )
|
|
{
|
|
for( i = 0; i < trainData->cols; i++ )
|
|
{
|
|
if( CV_MAT_ELEM( *trainData, float, compidx, i ) < threshold )
|
|
{
|
|
(*left)->data.fl[(*left)->cols++] = (float) i;
|
|
}
|
|
else
|
|
{
|
|
(*right)->data.fl[(*right)->cols++] = (float) i;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
uchar* idxdata;
|
|
int idxnum;
|
|
int idxstep;
|
|
int index;
|
|
|
|
idxdata = idx->data.ptr;
|
|
idxnum = (idx->rows == 1) ? idx->cols : idx->rows;
|
|
idxstep = (idx->rows == 1) ? CV_ELEM_SIZE( idx->type ) : idx->step;
|
|
for( i = 0; i < idxnum; i++ )
|
|
{
|
|
index = (int) *((float*) (idxdata + i * idxstep));
|
|
if( CV_MAT_ELEM( *trainData, float, compidx, index ) < threshold )
|
|
{
|
|
(*left)->data.fl[(*left)->cols++] = (float) index;
|
|
}
|
|
else
|
|
{
|
|
(*right)->data.fl[(*right)->cols++] = (float) index;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* internal structure used in CART creation */
|
|
typedef struct CvCARTNode
|
|
{
|
|
CvMat* sampleIdx;
|
|
CvStumpClassifier* stump;
|
|
int parent;
|
|
int leftflag;
|
|
float errdrop;
|
|
} CvCARTNode;
|
|
|
|
CV_BOOST_IMPL
|
|
CvClassifier* cvCreateCARTClassifier( CvMat* trainData,
|
|
int flags,
|
|
CvMat* trainClasses,
|
|
CvMat* typeMask,
|
|
CvMat* missedMeasurementsMask,
|
|
CvMat* compIdx,
|
|
CvMat* sampleIdx,
|
|
CvMat* weights,
|
|
CvClassifierTrainParams* trainParams )
|
|
{
|
|
CvCARTClassifier* cart = NULL;
|
|
size_t datasize = 0;
|
|
int count = 0;
|
|
int i = 0;
|
|
int j = 0;
|
|
|
|
CvCARTNode* intnode = NULL;
|
|
CvCARTNode* list = NULL;
|
|
int listcount = 0;
|
|
CvMat* lidx = NULL;
|
|
CvMat* ridx = NULL;
|
|
|
|
float maxerrdrop = 0.0F;
|
|
int idx = 0;
|
|
|
|
void (*splitIdxCallback)( int compidx, float threshold,
|
|
CvMat* idx, CvMat** left, CvMat** right,
|
|
void* userdata );
|
|
void* userdata;
|
|
|
|
count = ((CvCARTTrainParams*) trainParams)->count;
|
|
|
|
assert( count > 0 );
|
|
|
|
datasize = sizeof( *cart ) + (sizeof( float ) + 3 * sizeof( int )) * count +
|
|
sizeof( float ) * (count + 1);
|
|
|
|
cart = (CvCARTClassifier*) cvAlloc( datasize );
|
|
memset( cart, 0, datasize );
|
|
|
|
cart->count = count;
|
|
|
|
cart->eval = cvEvalCARTClassifier;
|
|
cart->save = NULL;
|
|
cart->release = cvReleaseCARTClassifier;
|
|
|
|
cart->compidx = (int*) (cart + 1);
|
|
cart->threshold = (float*) (cart->compidx + count);
|
|
cart->left = (int*) (cart->threshold + count);
|
|
cart->right = (int*) (cart->left + count);
|
|
cart->val = (float*) (cart->right + count);
|
|
|
|
datasize = sizeof( CvCARTNode ) * (count + count);
|
|
intnode = (CvCARTNode*) cvAlloc( datasize );
|
|
memset( intnode, 0, datasize );
|
|
list = (CvCARTNode*) (intnode + count);
|
|
|
|
splitIdxCallback = ((CvCARTTrainParams*) trainParams)->splitIdx;
|
|
userdata = ((CvCARTTrainParams*) trainParams)->userdata;
|
|
if( splitIdxCallback == NULL )
|
|
{
|
|
splitIdxCallback = ( CV_IS_ROW_SAMPLE( flags ) )
|
|
? icvDefaultSplitIdx_R : icvDefaultSplitIdx_C;
|
|
userdata = trainData;
|
|
}
|
|
|
|
/* create root of the tree */
|
|
intnode[0].sampleIdx = sampleIdx;
|
|
intnode[0].stump = (CvStumpClassifier*)
|
|
((CvCARTTrainParams*) trainParams)->stumpConstructor( trainData, flags,
|
|
trainClasses, typeMask, missedMeasurementsMask, compIdx, sampleIdx, weights,
|
|
((CvCARTTrainParams*) trainParams)->stumpTrainParams );
|
|
cart->left[0] = cart->right[0] = 0;
|
|
|
|
/* build tree */
|
|
listcount = 0;
|
|
for( i = 1; i < count; i++ )
|
|
{
|
|
/* split last added node */
|
|
splitIdxCallback( intnode[i-1].stump->compidx, intnode[i-1].stump->threshold,
|
|
intnode[i-1].sampleIdx, &lidx, &ridx, userdata );
|
|
|
|
if( intnode[i-1].stump->lerror != 0.0F )
|
|
{
|
|
list[listcount].sampleIdx = lidx;
|
|
list[listcount].stump = (CvStumpClassifier*)
|
|
((CvCARTTrainParams*) trainParams)->stumpConstructor( trainData, flags,
|
|
trainClasses, typeMask, missedMeasurementsMask, compIdx,
|
|
list[listcount].sampleIdx,
|
|
weights, ((CvCARTTrainParams*) trainParams)->stumpTrainParams );
|
|
list[listcount].errdrop = intnode[i-1].stump->lerror
|
|
- (list[listcount].stump->lerror + list[listcount].stump->rerror);
|
|
list[listcount].leftflag = 1;
|
|
list[listcount].parent = i-1;
|
|
listcount++;
|
|
}
|
|
else
|
|
{
|
|
cvReleaseMat( &lidx );
|
|
}
|
|
if( intnode[i-1].stump->rerror != 0.0F )
|
|
{
|
|
list[listcount].sampleIdx = ridx;
|
|
list[listcount].stump = (CvStumpClassifier*)
|
|
((CvCARTTrainParams*) trainParams)->stumpConstructor( trainData, flags,
|
|
trainClasses, typeMask, missedMeasurementsMask, compIdx,
|
|
list[listcount].sampleIdx,
|
|
weights, ((CvCARTTrainParams*) trainParams)->stumpTrainParams );
|
|
list[listcount].errdrop = intnode[i-1].stump->rerror
|
|
- (list[listcount].stump->lerror + list[listcount].stump->rerror);
|
|
list[listcount].leftflag = 0;
|
|
list[listcount].parent = i-1;
|
|
listcount++;
|
|
}
|
|
else
|
|
{
|
|
cvReleaseMat( &ridx );
|
|
}
|
|
|
|
if( listcount == 0 ) break;
|
|
|
|
/* find the best node to be added to the tree */
|
|
idx = 0;
|
|
maxerrdrop = list[idx].errdrop;
|
|
for( j = 1; j < listcount; j++ )
|
|
{
|
|
if( list[j].errdrop > maxerrdrop )
|
|
{
|
|
idx = j;
|
|
maxerrdrop = list[j].errdrop;
|
|
}
|
|
}
|
|
intnode[i] = list[idx];
|
|
if( list[idx].leftflag )
|
|
{
|
|
cart->left[list[idx].parent] = i;
|
|
}
|
|
else
|
|
{
|
|
cart->right[list[idx].parent] = i;
|
|
}
|
|
if( idx != (listcount - 1) )
|
|
{
|
|
list[idx] = list[listcount - 1];
|
|
}
|
|
listcount--;
|
|
}
|
|
|
|
/* fill <cart> fields */
|
|
j = 0;
|
|
cart->count = 0;
|
|
for( i = 0; i < count && (intnode[i].stump != NULL); i++ )
|
|
{
|
|
cart->count++;
|
|
cart->compidx[i] = intnode[i].stump->compidx;
|
|
cart->threshold[i] = intnode[i].stump->threshold;
|
|
|
|
/* leaves */
|
|
if( cart->left[i] <= 0 )
|
|
{
|
|
cart->left[i] = -j;
|
|
cart->val[j] = intnode[i].stump->left;
|
|
j++;
|
|
}
|
|
if( cart->right[i] <= 0 )
|
|
{
|
|
cart->right[i] = -j;
|
|
cart->val[j] = intnode[i].stump->right;
|
|
j++;
|
|
}
|
|
}
|
|
|
|
/* CLEAN UP */
|
|
for( i = 0; i < count && (intnode[i].stump != NULL); i++ )
|
|
{
|
|
intnode[i].stump->release( (CvClassifier**) &(intnode[i].stump) );
|
|
if( i != 0 )
|
|
{
|
|
cvReleaseMat( &(intnode[i].sampleIdx) );
|
|
}
|
|
}
|
|
for( i = 0; i < listcount; i++ )
|
|
{
|
|
list[i].stump->release( (CvClassifier**) &(list[i].stump) );
|
|
cvReleaseMat( &(list[i].sampleIdx) );
|
|
}
|
|
|
|
cvFree( &intnode );
|
|
|
|
return (CvClassifier*) cart;
|
|
}
|
|
|
|
/****************************************************************************************\
|
|
* Boosting *
|
|
\****************************************************************************************/
|
|
|
|
typedef struct CvBoostTrainer
|
|
{
|
|
CvBoostType type;
|
|
int count; /* (idx) ? number_of_indices : number_of_samples */
|
|
int* idx;
|
|
float* F;
|
|
} CvBoostTrainer;
|
|
|
|
/*
|
|
* cvBoostStartTraining, cvBoostNextWeakClassifier, cvBoostEndTraining
|
|
*
|
|
* These functions perform training of 2-class boosting classifier
|
|
* using ANY appropriate weak classifier
|
|
*/
|
|
|
|
static
|
|
CvBoostTrainer* icvBoostStartTraining( CvMat* trainClasses,
|
|
CvMat* weakTrainVals,
|
|
CvMat* /*weights*/,
|
|
CvMat* sampleIdx,
|
|
CvBoostType type )
|
|
{
|
|
uchar* ydata;
|
|
int ystep;
|
|
int m;
|
|
uchar* traindata;
|
|
int trainstep;
|
|
int trainnum;
|
|
int i;
|
|
int idx;
|
|
|
|
size_t datasize;
|
|
CvBoostTrainer* ptr;
|
|
|
|
int idxnum;
|
|
int idxstep;
|
|
uchar* idxdata;
|
|
|
|
assert( trainClasses != NULL );
|
|
assert( CV_MAT_TYPE( trainClasses->type ) == CV_32FC1 );
|
|
assert( weakTrainVals != NULL );
|
|
assert( CV_MAT_TYPE( weakTrainVals->type ) == CV_32FC1 );
|
|
|
|
CV_MAT2VEC( *trainClasses, ydata, ystep, m );
|
|
CV_MAT2VEC( *weakTrainVals, traindata, trainstep, trainnum );
|
|
|
|
CV_Assert( m == trainnum );
|
|
|
|
idxnum = 0;
|
|
idxstep = 0;
|
|
idxdata = NULL;
|
|
if( sampleIdx )
|
|
{
|
|
CV_MAT2VEC( *sampleIdx, idxdata, idxstep, idxnum );
|
|
}
|
|
|
|
datasize = sizeof( *ptr ) + sizeof( *ptr->idx ) * idxnum;
|
|
ptr = (CvBoostTrainer*) cvAlloc( datasize );
|
|
memset( ptr, 0, datasize );
|
|
ptr->F = NULL;
|
|
ptr->idx = NULL;
|
|
|
|
ptr->count = m;
|
|
ptr->type = type;
|
|
|
|
if( idxnum > 0 )
|
|
{
|
|
CvScalar s;
|
|
|
|
ptr->idx = (int*) (ptr + 1);
|
|
ptr->count = idxnum;
|
|
for( i = 0; i < ptr->count; i++ )
|
|
{
|
|
cvRawDataToScalar( idxdata + i*idxstep, CV_MAT_TYPE( sampleIdx->type ), &s );
|
|
ptr->idx[i] = (int) s.val[0];
|
|
}
|
|
}
|
|
for( i = 0; i < ptr->count; i++ )
|
|
{
|
|
idx = (ptr->idx) ? ptr->idx[i] : i;
|
|
|
|
*((float*) (traindata + idx * trainstep)) =
|
|
2.0F * (*((float*) (ydata + idx * ystep))) - 1.0F;
|
|
}
|
|
|
|
return ptr;
|
|
}
|
|
|
|
/*
|
|
*
|
|
* Discrete AdaBoost functions
|
|
*
|
|
*/
|
|
static
|
|
float icvBoostNextWeakClassifierDAB( CvMat* weakEvalVals,
|
|
CvMat* trainClasses,
|
|
CvMat* /*weakTrainVals*/,
|
|
CvMat* weights,
|
|
CvBoostTrainer* trainer )
|
|
{
|
|
uchar* evaldata;
|
|
int evalstep;
|
|
int m;
|
|
uchar* ydata;
|
|
int ystep;
|
|
int ynum;
|
|
uchar* wdata;
|
|
int wstep;
|
|
int wnum;
|
|
|
|
float sumw;
|
|
float err;
|
|
int i;
|
|
int idx;
|
|
|
|
CV_Assert( weakEvalVals != NULL );
|
|
CV_Assert( CV_MAT_TYPE( weakEvalVals->type ) == CV_32FC1 );
|
|
CV_Assert( trainClasses != NULL );
|
|
CV_Assert( CV_MAT_TYPE( trainClasses->type ) == CV_32FC1 );
|
|
CV_Assert( weights != NULL );
|
|
CV_Assert( CV_MAT_TYPE( weights ->type ) == CV_32FC1 );
|
|
|
|
CV_MAT2VEC( *weakEvalVals, evaldata, evalstep, m );
|
|
CV_MAT2VEC( *trainClasses, ydata, ystep, ynum );
|
|
CV_MAT2VEC( *weights, wdata, wstep, wnum );
|
|
|
|
CV_Assert( m == ynum );
|
|
CV_Assert( m == wnum );
|
|
|
|
sumw = 0.0F;
|
|
err = 0.0F;
|
|
for( i = 0; i < trainer->count; i++ )
|
|
{
|
|
idx = (trainer->idx) ? trainer->idx[i] : i;
|
|
|
|
sumw += *((float*) (wdata + idx*wstep));
|
|
err += (*((float*) (wdata + idx*wstep))) *
|
|
( (*((float*) (evaldata + idx*evalstep))) !=
|
|
2.0F * (*((float*) (ydata + idx*ystep))) - 1.0F );
|
|
}
|
|
err /= sumw;
|
|
err = -cvLogRatio( err );
|
|
|
|
for( i = 0; i < trainer->count; i++ )
|
|
{
|
|
idx = (trainer->idx) ? trainer->idx[i] : i;
|
|
|
|
*((float*) (wdata + idx*wstep)) *= expf( err *
|
|
((*((float*) (evaldata + idx*evalstep))) !=
|
|
2.0F * (*((float*) (ydata + idx*ystep))) - 1.0F) );
|
|
sumw += *((float*) (wdata + idx*wstep));
|
|
}
|
|
for( i = 0; i < trainer->count; i++ )
|
|
{
|
|
idx = (trainer->idx) ? trainer->idx[i] : i;
|
|
|
|
*((float*) (wdata + idx * wstep)) /= sumw;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
*
|
|
* Real AdaBoost functions
|
|
*
|
|
*/
|
|
static
|
|
float icvBoostNextWeakClassifierRAB( CvMat* weakEvalVals,
|
|
CvMat* trainClasses,
|
|
CvMat* /*weakTrainVals*/,
|
|
CvMat* weights,
|
|
CvBoostTrainer* trainer )
|
|
{
|
|
uchar* evaldata;
|
|
int evalstep;
|
|
int m;
|
|
uchar* ydata;
|
|
int ystep;
|
|
int ynum;
|
|
uchar* wdata;
|
|
int wstep;
|
|
int wnum;
|
|
|
|
float sumw;
|
|
int i, idx;
|
|
|
|
CV_Assert( weakEvalVals != NULL );
|
|
CV_Assert( CV_MAT_TYPE( weakEvalVals->type ) == CV_32FC1 );
|
|
CV_Assert( trainClasses != NULL );
|
|
CV_Assert( CV_MAT_TYPE( trainClasses->type ) == CV_32FC1 );
|
|
CV_Assert( weights != NULL );
|
|
CV_Assert( CV_MAT_TYPE( weights ->type ) == CV_32FC1 );
|
|
|
|
CV_MAT2VEC( *weakEvalVals, evaldata, evalstep, m );
|
|
CV_MAT2VEC( *trainClasses, ydata, ystep, ynum );
|
|
CV_MAT2VEC( *weights, wdata, wstep, wnum );
|
|
|
|
CV_Assert( m == ynum );
|
|
CV_Assert( m == wnum );
|
|
|
|
|
|
sumw = 0.0F;
|
|
for( i = 0; i < trainer->count; i++ )
|
|
{
|
|
idx = (trainer->idx) ? trainer->idx[i] : i;
|
|
|
|
*((float*) (wdata + idx*wstep)) *= expf( (-(*((float*) (ydata + idx*ystep))) + 0.5F)
|
|
* cvLogRatio( *((float*) (evaldata + idx*evalstep)) ) );
|
|
sumw += *((float*) (wdata + idx*wstep));
|
|
}
|
|
for( i = 0; i < trainer->count; i++ )
|
|
{
|
|
idx = (trainer->idx) ? trainer->idx[i] : i;
|
|
|
|
*((float*) (wdata + idx*wstep)) /= sumw;
|
|
}
|
|
|
|
return 1.0F;
|
|
}
|
|
|
|
/*
|
|
*
|
|
* LogitBoost functions
|
|
*
|
|
*/
|
|
#define CV_LB_PROB_THRESH 0.01F
|
|
#define CV_LB_WEIGHT_THRESHOLD 0.0001F
|
|
|
|
static
|
|
void icvResponsesAndWeightsLB( int num, uchar* wdata, int wstep,
|
|
uchar* ydata, int ystep,
|
|
uchar* fdata, int fstep,
|
|
uchar* traindata, int trainstep,
|
|
int* indices )
|
|
{
|
|
int i, idx;
|
|
float p;
|
|
|
|
for( i = 0; i < num; i++ )
|
|
{
|
|
idx = (indices) ? indices[i] : i;
|
|
|
|
p = 1.0F / (1.0F + expf( -(*((float*) (fdata + idx*fstep)))) );
|
|
*((float*) (wdata + idx*wstep)) = MAX( p * (1.0F - p), CV_LB_WEIGHT_THRESHOLD );
|
|
if( *((float*) (ydata + idx*ystep)) == 1.0F )
|
|
{
|
|
*((float*) (traindata + idx*trainstep)) =
|
|
1.0F / (MAX( p, CV_LB_PROB_THRESH ));
|
|
}
|
|
else
|
|
{
|
|
*((float*) (traindata + idx*trainstep)) =
|
|
-1.0F / (MAX( 1.0F - p, CV_LB_PROB_THRESH ));
|
|
}
|
|
}
|
|
}
|
|
|
|
static
|
|
CvBoostTrainer* icvBoostStartTrainingLB( CvMat* trainClasses,
|
|
CvMat* weakTrainVals,
|
|
CvMat* weights,
|
|
CvMat* sampleIdx,
|
|
CvBoostType type )
|
|
{
|
|
size_t datasize;
|
|
CvBoostTrainer* ptr;
|
|
|
|
uchar* ydata;
|
|
int ystep;
|
|
int m;
|
|
uchar* traindata;
|
|
int trainstep;
|
|
int trainnum;
|
|
uchar* wdata;
|
|
int wstep;
|
|
int wnum;
|
|
int i;
|
|
|
|
int idxnum;
|
|
int idxstep;
|
|
uchar* idxdata;
|
|
|
|
assert( trainClasses != NULL );
|
|
assert( CV_MAT_TYPE( trainClasses->type ) == CV_32FC1 );
|
|
assert( weakTrainVals != NULL );
|
|
assert( CV_MAT_TYPE( weakTrainVals->type ) == CV_32FC1 );
|
|
assert( weights != NULL );
|
|
assert( CV_MAT_TYPE( weights->type ) == CV_32FC1 );
|
|
|
|
CV_MAT2VEC( *trainClasses, ydata, ystep, m );
|
|
CV_MAT2VEC( *weakTrainVals, traindata, trainstep, trainnum );
|
|
CV_MAT2VEC( *weights, wdata, wstep, wnum );
|
|
|
|
CV_Assert( m == trainnum );
|
|
CV_Assert( m == wnum );
|
|
|
|
|
|
idxnum = 0;
|
|
idxstep = 0;
|
|
idxdata = NULL;
|
|
if( sampleIdx )
|
|
{
|
|
CV_MAT2VEC( *sampleIdx, idxdata, idxstep, idxnum );
|
|
}
|
|
|
|
datasize = sizeof( *ptr ) + sizeof( *ptr->F ) * m + sizeof( *ptr->idx ) * idxnum;
|
|
ptr = (CvBoostTrainer*) cvAlloc( datasize );
|
|
memset( ptr, 0, datasize );
|
|
ptr->F = (float*) (ptr + 1);
|
|
ptr->idx = NULL;
|
|
|
|
ptr->count = m;
|
|
ptr->type = type;
|
|
|
|
if( idxnum > 0 )
|
|
{
|
|
CvScalar s;
|
|
|
|
ptr->idx = (int*) (ptr->F + m);
|
|
ptr->count = idxnum;
|
|
for( i = 0; i < ptr->count; i++ )
|
|
{
|
|
cvRawDataToScalar( idxdata + i*idxstep, CV_MAT_TYPE( sampleIdx->type ), &s );
|
|
ptr->idx[i] = (int) s.val[0];
|
|
}
|
|
}
|
|
|
|
for( i = 0; i < m; i++ )
|
|
{
|
|
ptr->F[i] = 0.0F;
|
|
}
|
|
|
|
icvResponsesAndWeightsLB( ptr->count, wdata, wstep, ydata, ystep,
|
|
(uchar*) ptr->F, sizeof( *ptr->F ),
|
|
traindata, trainstep, ptr->idx );
|
|
|
|
return ptr;
|
|
}
|
|
|
|
static
|
|
float icvBoostNextWeakClassifierLB( CvMat* weakEvalVals,
|
|
CvMat* trainClasses,
|
|
CvMat* weakTrainVals,
|
|
CvMat* weights,
|
|
CvBoostTrainer* trainer )
|
|
{
|
|
uchar* evaldata;
|
|
int evalstep;
|
|
int m;
|
|
uchar* ydata;
|
|
int ystep;
|
|
int ynum;
|
|
uchar* traindata;
|
|
int trainstep;
|
|
int trainnum;
|
|
uchar* wdata;
|
|
int wstep;
|
|
int wnum;
|
|
int i, idx;
|
|
|
|
assert( weakEvalVals != NULL );
|
|
assert( CV_MAT_TYPE( weakEvalVals->type ) == CV_32FC1 );
|
|
assert( trainClasses != NULL );
|
|
assert( CV_MAT_TYPE( trainClasses->type ) == CV_32FC1 );
|
|
assert( weakTrainVals != NULL );
|
|
assert( CV_MAT_TYPE( weakTrainVals->type ) == CV_32FC1 );
|
|
assert( weights != NULL );
|
|
assert( CV_MAT_TYPE( weights ->type ) == CV_32FC1 );
|
|
|
|
CV_MAT2VEC( *weakEvalVals, evaldata, evalstep, m );
|
|
CV_MAT2VEC( *trainClasses, ydata, ystep, ynum );
|
|
CV_MAT2VEC( *weakTrainVals, traindata, trainstep, trainnum );
|
|
CV_MAT2VEC( *weights, wdata, wstep, wnum );
|
|
|
|
CV_Assert( m == ynum );
|
|
CV_Assert( m == wnum );
|
|
CV_Assert( m == trainnum );
|
|
//assert( m == trainer->count );
|
|
|
|
for( i = 0; i < trainer->count; i++ )
|
|
{
|
|
idx = (trainer->idx) ? trainer->idx[i] : i;
|
|
|
|
trainer->F[idx] += *((float*) (evaldata + idx * evalstep));
|
|
}
|
|
|
|
icvResponsesAndWeightsLB( trainer->count, wdata, wstep, ydata, ystep,
|
|
(uchar*) trainer->F, sizeof( *trainer->F ),
|
|
traindata, trainstep, trainer->idx );
|
|
|
|
return 1.0F;
|
|
}
|
|
|
|
/*
|
|
*
|
|
* Gentle AdaBoost
|
|
*
|
|
*/
|
|
static
|
|
float icvBoostNextWeakClassifierGAB( CvMat* weakEvalVals,
|
|
CvMat* trainClasses,
|
|
CvMat* /*weakTrainVals*/,
|
|
CvMat* weights,
|
|
CvBoostTrainer* trainer )
|
|
{
|
|
uchar* evaldata;
|
|
int evalstep;
|
|
int m;
|
|
uchar* ydata;
|
|
int ystep;
|
|
int ynum;
|
|
uchar* wdata;
|
|
int wstep;
|
|
int wnum;
|
|
|
|
int i, idx;
|
|
float sumw;
|
|
|
|
CV_Assert( weakEvalVals != NULL );
|
|
CV_Assert( CV_MAT_TYPE( weakEvalVals->type ) == CV_32FC1 );
|
|
CV_Assert( trainClasses != NULL );
|
|
CV_Assert( CV_MAT_TYPE( trainClasses->type ) == CV_32FC1 );
|
|
CV_Assert( weights != NULL );
|
|
CV_Assert( CV_MAT_TYPE( weights->type ) == CV_32FC1 );
|
|
|
|
CV_MAT2VEC( *weakEvalVals, evaldata, evalstep, m );
|
|
CV_MAT2VEC( *trainClasses, ydata, ystep, ynum );
|
|
CV_MAT2VEC( *weights, wdata, wstep, wnum );
|
|
|
|
CV_Assert( m == ynum );
|
|
CV_Assert( m == wnum );
|
|
|
|
sumw = 0.0F;
|
|
for( i = 0; i < trainer->count; i++ )
|
|
{
|
|
idx = (trainer->idx) ? trainer->idx[i] : i;
|
|
|
|
*((float*) (wdata + idx*wstep)) *=
|
|
expf( -(*((float*) (evaldata + idx*evalstep)))
|
|
* ( 2.0F * (*((float*) (ydata + idx*ystep))) - 1.0F ) );
|
|
sumw += *((float*) (wdata + idx*wstep));
|
|
}
|
|
|
|
for( i = 0; i < trainer->count; i++ )
|
|
{
|
|
idx = (trainer->idx) ? trainer->idx[i] : i;
|
|
|
|
*((float*) (wdata + idx*wstep)) /= sumw;
|
|
}
|
|
|
|
return 1.0F;
|
|
}
|
|
|
|
typedef CvBoostTrainer* (*CvBoostStartTraining)( CvMat* trainClasses,
|
|
CvMat* weakTrainVals,
|
|
CvMat* weights,
|
|
CvMat* sampleIdx,
|
|
CvBoostType type );
|
|
|
|
typedef float (*CvBoostNextWeakClassifier)( CvMat* weakEvalVals,
|
|
CvMat* trainClasses,
|
|
CvMat* weakTrainVals,
|
|
CvMat* weights,
|
|
CvBoostTrainer* data );
|
|
|
|
CvBoostStartTraining startTraining[4] = {
|
|
icvBoostStartTraining,
|
|
icvBoostStartTraining,
|
|
icvBoostStartTrainingLB,
|
|
icvBoostStartTraining
|
|
};
|
|
|
|
CvBoostNextWeakClassifier nextWeakClassifier[4] = {
|
|
icvBoostNextWeakClassifierDAB,
|
|
icvBoostNextWeakClassifierRAB,
|
|
icvBoostNextWeakClassifierLB,
|
|
icvBoostNextWeakClassifierGAB
|
|
};
|
|
|
|
/*
|
|
*
|
|
* Dispatchers
|
|
*
|
|
*/
|
|
CV_BOOST_IMPL
|
|
CvBoostTrainer* cvBoostStartTraining( CvMat* trainClasses,
|
|
CvMat* weakTrainVals,
|
|
CvMat* weights,
|
|
CvMat* sampleIdx,
|
|
CvBoostType type )
|
|
{
|
|
return startTraining[type]( trainClasses, weakTrainVals, weights, sampleIdx, type );
|
|
}
|
|
|
|
CV_BOOST_IMPL
|
|
void cvBoostEndTraining( CvBoostTrainer** trainer )
|
|
{
|
|
cvFree( trainer );
|
|
*trainer = NULL;
|
|
}
|
|
|
|
CV_BOOST_IMPL
|
|
float cvBoostNextWeakClassifier( CvMat* weakEvalVals,
|
|
CvMat* trainClasses,
|
|
CvMat* weakTrainVals,
|
|
CvMat* weights,
|
|
CvBoostTrainer* trainer )
|
|
{
|
|
return nextWeakClassifier[trainer->type]( weakEvalVals, trainClasses,
|
|
weakTrainVals, weights, trainer );
|
|
}
|
|
|
|
/****************************************************************************************\
|
|
* Boosted tree models *
|
|
\****************************************************************************************/
|
|
|
|
typedef struct CvBtTrainer
|
|
{
|
|
/* {{ external */
|
|
CvMat* trainData;
|
|
int flags;
|
|
|
|
CvMat* trainClasses;
|
|
int m;
|
|
uchar* ydata;
|
|
int ystep;
|
|
|
|
CvMat* sampleIdx;
|
|
int numsamples;
|
|
|
|
float param[2];
|
|
CvBoostType type;
|
|
int numclasses;
|
|
/* }} external */
|
|
|
|
CvMTStumpTrainParams stumpParams;
|
|
CvCARTTrainParams cartParams;
|
|
|
|
float* f; /* F_(m-1) */
|
|
CvMat* y; /* yhat */
|
|
CvMat* weights;
|
|
CvBoostTrainer* boosttrainer;
|
|
} CvBtTrainer;
|
|
|
|
/*
|
|
* cvBtStart, cvBtNext, cvBtEnd
|
|
*
|
|
* These functions perform iterative training of
|
|
* 2-class (CV_DABCLASS - CV_GABCLASS, CV_L2CLASS), K-class (CV_LKCLASS) classifier
|
|
* or fit regression model (CV_LSREG, CV_LADREG, CV_MREG)
|
|
* using decision tree as a weak classifier.
|
|
*/
|
|
|
|
typedef void (*CvZeroApproxFunc)( float* approx, CvBtTrainer* trainer );
|
|
|
|
/* Mean zero approximation */
|
|
static void icvZeroApproxMean( float* approx, CvBtTrainer* trainer )
|
|
{
|
|
int i;
|
|
int idx;
|
|
|
|
approx[0] = 0.0F;
|
|
for( i = 0; i < trainer->numsamples; i++ )
|
|
{
|
|
idx = icvGetIdxAt( trainer->sampleIdx, i );
|
|
approx[0] += *((float*) (trainer->ydata + idx * trainer->ystep));
|
|
}
|
|
approx[0] /= (float) trainer->numsamples;
|
|
}
|
|
|
|
/*
|
|
* Median zero approximation
|
|
*/
|
|
static void icvZeroApproxMed( float* approx, CvBtTrainer* trainer )
|
|
{
|
|
int i;
|
|
int idx;
|
|
|
|
for( i = 0; i < trainer->numsamples; i++ )
|
|
{
|
|
idx = icvGetIdxAt( trainer->sampleIdx, i );
|
|
trainer->f[i] = *((float*) (trainer->ydata + idx * trainer->ystep));
|
|
}
|
|
|
|
icvSort_32f( trainer->f, trainer->numsamples, 0 );
|
|
approx[0] = trainer->f[trainer->numsamples / 2];
|
|
}
|
|
|
|
/*
|
|
* 0.5 * log( mean(y) / (1 - mean(y)) ) where y in {0, 1}
|
|
*/
|
|
static void icvZeroApproxLog( float* approx, CvBtTrainer* trainer )
|
|
{
|
|
float y_mean;
|
|
|
|
icvZeroApproxMean( &y_mean, trainer );
|
|
approx[0] = 0.5F * cvLogRatio( y_mean );
|
|
}
|
|
|
|
/*
|
|
* 0 zero approximation
|
|
*/
|
|
static void icvZeroApprox0( float* approx, CvBtTrainer* trainer )
|
|
{
|
|
int i;
|
|
|
|
for( i = 0; i < trainer->numclasses; i++ )
|
|
{
|
|
approx[i] = 0.0F;
|
|
}
|
|
}
|
|
|
|
static CvZeroApproxFunc icvZeroApproxFunc[] =
|
|
{
|
|
icvZeroApprox0, /* CV_DABCLASS */
|
|
icvZeroApprox0, /* CV_RABCLASS */
|
|
icvZeroApprox0, /* CV_LBCLASS */
|
|
icvZeroApprox0, /* CV_GABCLASS */
|
|
icvZeroApproxLog, /* CV_L2CLASS */
|
|
icvZeroApprox0, /* CV_LKCLASS */
|
|
icvZeroApproxMean, /* CV_LSREG */
|
|
icvZeroApproxMed, /* CV_LADREG */
|
|
icvZeroApproxMed, /* CV_MREG */
|
|
};
|
|
|
|
CV_BOOST_IMPL
|
|
void cvBtNext( CvCARTClassifier** trees, CvBtTrainer* trainer );
|
|
|
|
static
|
|
CvBtTrainer* cvBtStart( CvCARTClassifier** trees,
|
|
CvMat* trainData,
|
|
int flags,
|
|
CvMat* trainClasses,
|
|
CvMat* sampleIdx,
|
|
int numsplits,
|
|
CvBoostType type,
|
|
int numclasses,
|
|
float* param )
|
|
{
|
|
CvBtTrainer* ptr = 0;
|
|
|
|
CV_FUNCNAME( "cvBtStart" );
|
|
|
|
__BEGIN__;
|
|
|
|
size_t data_size;
|
|
float* zero_approx;
|
|
int m;
|
|
int i, j;
|
|
|
|
if( trees == NULL )
|
|
{
|
|
CV_ERROR( CV_StsNullPtr, "Invalid trees parameter" );
|
|
}
|
|
|
|
if( type < CV_DABCLASS || type > CV_MREG )
|
|
{
|
|
CV_ERROR( CV_StsUnsupportedFormat, "Unsupported type parameter" );
|
|
}
|
|
if( type == CV_LKCLASS )
|
|
{
|
|
CV_ASSERT( numclasses >= 2 );
|
|
}
|
|
else
|
|
{
|
|
numclasses = 1;
|
|
}
|
|
|
|
m = MAX( trainClasses->rows, trainClasses->cols );
|
|
ptr = NULL;
|
|
data_size = sizeof( *ptr );
|
|
if( type > CV_GABCLASS )
|
|
{
|
|
data_size += m * numclasses * sizeof( *(ptr->f) );
|
|
}
|
|
CV_CALL( ptr = (CvBtTrainer*) cvAlloc( data_size ) );
|
|
memset( ptr, 0, data_size );
|
|
ptr->f = (float*) (ptr + 1);
|
|
|
|
ptr->trainData = trainData;
|
|
ptr->flags = flags;
|
|
ptr->trainClasses = trainClasses;
|
|
CV_MAT2VEC( *trainClasses, ptr->ydata, ptr->ystep, ptr->m );
|
|
|
|
memset( &(ptr->cartParams), 0, sizeof( ptr->cartParams ) );
|
|
memset( &(ptr->stumpParams), 0, sizeof( ptr->stumpParams ) );
|
|
|
|
switch( type )
|
|
{
|
|
case CV_DABCLASS:
|
|
ptr->stumpParams.error = CV_MISCLASSIFICATION;
|
|
ptr->stumpParams.type = CV_CLASSIFICATION_CLASS;
|
|
break;
|
|
case CV_RABCLASS:
|
|
ptr->stumpParams.error = CV_GINI;
|
|
ptr->stumpParams.type = CV_CLASSIFICATION;
|
|
break;
|
|
default:
|
|
ptr->stumpParams.error = CV_SQUARE;
|
|
ptr->stumpParams.type = CV_REGRESSION;
|
|
}
|
|
ptr->cartParams.count = numsplits;
|
|
ptr->cartParams.stumpTrainParams = (CvClassifierTrainParams*) &(ptr->stumpParams);
|
|
ptr->cartParams.stumpConstructor = cvCreateMTStumpClassifier;
|
|
|
|
ptr->param[0] = param[0];
|
|
ptr->param[1] = param[1];
|
|
ptr->type = type;
|
|
ptr->numclasses = numclasses;
|
|
|
|
CV_CALL( ptr->y = cvCreateMat( 1, m, CV_32FC1 ) );
|
|
ptr->sampleIdx = sampleIdx;
|
|
ptr->numsamples = ( sampleIdx == NULL ) ? ptr->m
|
|
: MAX( sampleIdx->rows, sampleIdx->cols );
|
|
|
|
ptr->weights = cvCreateMat( 1, m, CV_32FC1 );
|
|
cvSet( ptr->weights, cvScalar( 1.0 ) );
|
|
|
|
if( type <= CV_GABCLASS )
|
|
{
|
|
ptr->boosttrainer = cvBoostStartTraining( ptr->trainClasses, ptr->y,
|
|
ptr->weights, NULL, type );
|
|
|
|
CV_CALL( cvBtNext( trees, ptr ) );
|
|
}
|
|
else
|
|
{
|
|
data_size = sizeof( *zero_approx ) * numclasses;
|
|
CV_CALL( zero_approx = (float*) cvAlloc( data_size ) );
|
|
icvZeroApproxFunc[type]( zero_approx, ptr );
|
|
for( i = 0; i < m; i++ )
|
|
{
|
|
for( j = 0; j < numclasses; j++ )
|
|
{
|
|
ptr->f[i * numclasses + j] = zero_approx[j];
|
|
}
|
|
}
|
|
|
|
CV_CALL( cvBtNext( trees, ptr ) );
|
|
|
|
for( i = 0; i < numclasses; i++ )
|
|
{
|
|
for( j = 0; j <= trees[i]->count; j++ )
|
|
{
|
|
trees[i]->val[j] += zero_approx[i];
|
|
}
|
|
}
|
|
CV_CALL( cvFree( &zero_approx ) );
|
|
}
|
|
|
|
__END__;
|
|
|
|
return ptr;
|
|
}
|
|
|
|
static void icvBtNext_LSREG( CvCARTClassifier** trees, CvBtTrainer* trainer )
|
|
{
|
|
int i;
|
|
|
|
/* yhat_i = y_i - F_(m-1)(x_i) */
|
|
for( i = 0; i < trainer->m; i++ )
|
|
{
|
|
trainer->y->data.fl[i] =
|
|
*((float*) (trainer->ydata + i * trainer->ystep)) - trainer->f[i];
|
|
}
|
|
|
|
trees[0] = (CvCARTClassifier*) cvCreateCARTClassifier( trainer->trainData,
|
|
trainer->flags,
|
|
trainer->y, NULL, NULL, NULL, trainer->sampleIdx, trainer->weights,
|
|
(CvClassifierTrainParams*) &trainer->cartParams );
|
|
}
|
|
|
|
|
|
static void icvBtNext_LADREG( CvCARTClassifier** trees, CvBtTrainer* trainer )
|
|
{
|
|
CvCARTClassifier* ptr;
|
|
int i, j;
|
|
CvMat sample;
|
|
int sample_step;
|
|
uchar* sample_data;
|
|
int index;
|
|
|
|
int data_size;
|
|
int* idx;
|
|
float* resp;
|
|
int respnum;
|
|
float val;
|
|
|
|
data_size = trainer->m * sizeof( *idx );
|
|
idx = (int*) cvAlloc( data_size );
|
|
data_size = trainer->m * sizeof( *resp );
|
|
resp = (float*) cvAlloc( data_size );
|
|
|
|
/* yhat_i = sign(y_i - F_(m-1)(x_i)) */
|
|
for( i = 0; i < trainer->numsamples; i++ )
|
|
{
|
|
index = icvGetIdxAt( trainer->sampleIdx, i );
|
|
trainer->y->data.fl[index] = (float)
|
|
CV_SIGN( *((float*) (trainer->ydata + index * trainer->ystep))
|
|
- trainer->f[index] );
|
|
}
|
|
|
|
ptr = (CvCARTClassifier*) cvCreateCARTClassifier( trainer->trainData, trainer->flags,
|
|
trainer->y, NULL, NULL, NULL, trainer->sampleIdx, trainer->weights,
|
|
(CvClassifierTrainParams*) &trainer->cartParams );
|
|
|
|
CV_GET_SAMPLE( *trainer->trainData, trainer->flags, 0, sample );
|
|
CV_GET_SAMPLE_STEP( *trainer->trainData, trainer->flags, sample_step );
|
|
sample_data = sample.data.ptr;
|
|
for( i = 0; i < trainer->numsamples; i++ )
|
|
{
|
|
index = icvGetIdxAt( trainer->sampleIdx, i );
|
|
sample.data.ptr = sample_data + index * sample_step;
|
|
idx[index] = (int) cvEvalCARTClassifierIdx( (CvClassifier*) ptr, &sample );
|
|
}
|
|
for( j = 0; j <= ptr->count; j++ )
|
|
{
|
|
respnum = 0;
|
|
for( i = 0; i < trainer->numsamples; i++ )
|
|
{
|
|
index = icvGetIdxAt( trainer->sampleIdx, i );
|
|
if( idx[index] == j )
|
|
{
|
|
resp[respnum++] = *((float*) (trainer->ydata + index * trainer->ystep))
|
|
- trainer->f[index];
|
|
}
|
|
}
|
|
if( respnum > 0 )
|
|
{
|
|
icvSort_32f( resp, respnum, 0 );
|
|
val = resp[respnum / 2];
|
|
}
|
|
else
|
|
{
|
|
val = 0.0F;
|
|
}
|
|
ptr->val[j] = val;
|
|
}
|
|
|
|
cvFree( &idx );
|
|
cvFree( &resp );
|
|
|
|
trees[0] = ptr;
|
|
}
|
|
|
|
|
|
static void icvBtNext_MREG( CvCARTClassifier** trees, CvBtTrainer* trainer )
|
|
{
|
|
CvCARTClassifier* ptr;
|
|
int i, j;
|
|
CvMat sample;
|
|
int sample_step;
|
|
uchar* sample_data;
|
|
|
|
int data_size;
|
|
int* idx;
|
|
float* resid;
|
|
float* resp;
|
|
int respnum;
|
|
float rhat;
|
|
float val;
|
|
float delta;
|
|
int index;
|
|
|
|
data_size = trainer->m * sizeof( *idx );
|
|
idx = (int*) cvAlloc( data_size );
|
|
data_size = trainer->m * sizeof( *resp );
|
|
resp = (float*) cvAlloc( data_size );
|
|
data_size = trainer->m * sizeof( *resid );
|
|
resid = (float*) cvAlloc( data_size );
|
|
|
|
/* resid_i = (y_i - F_(m-1)(x_i)) */
|
|
for( i = 0; i < trainer->numsamples; i++ )
|
|
{
|
|
index = icvGetIdxAt( trainer->sampleIdx, i );
|
|
resid[index] = *((float*) (trainer->ydata + index * trainer->ystep))
|
|
- trainer->f[index];
|
|
/* for delta */
|
|
resp[i] = (float) fabs( resid[index] );
|
|
}
|
|
|
|
/* delta = quantile_alpha{abs(resid_i)} */
|
|
icvSort_32f( resp, trainer->numsamples, 0 );
|
|
delta = resp[(int)(trainer->param[1] * (trainer->numsamples - 1))];
|
|
|
|
/* yhat_i */
|
|
for( i = 0; i < trainer->numsamples; i++ )
|
|
{
|
|
index = icvGetIdxAt( trainer->sampleIdx, i );
|
|
trainer->y->data.fl[index] = MIN( delta, ((float) fabs( resid[index] )) ) *
|
|
CV_SIGN( resid[index] );
|
|
}
|
|
|
|
ptr = (CvCARTClassifier*) cvCreateCARTClassifier( trainer->trainData, trainer->flags,
|
|
trainer->y, NULL, NULL, NULL, trainer->sampleIdx, trainer->weights,
|
|
(CvClassifierTrainParams*) &trainer->cartParams );
|
|
|
|
CV_GET_SAMPLE( *trainer->trainData, trainer->flags, 0, sample );
|
|
CV_GET_SAMPLE_STEP( *trainer->trainData, trainer->flags, sample_step );
|
|
sample_data = sample.data.ptr;
|
|
for( i = 0; i < trainer->numsamples; i++ )
|
|
{
|
|
index = icvGetIdxAt( trainer->sampleIdx, i );
|
|
sample.data.ptr = sample_data + index * sample_step;
|
|
idx[index] = (int) cvEvalCARTClassifierIdx( (CvClassifier*) ptr, &sample );
|
|
}
|
|
for( j = 0; j <= ptr->count; j++ )
|
|
{
|
|
respnum = 0;
|
|
|
|
for( i = 0; i < trainer->numsamples; i++ )
|
|
{
|
|
index = icvGetIdxAt( trainer->sampleIdx, i );
|
|
if( idx[index] == j )
|
|
{
|
|
resp[respnum++] = *((float*) (trainer->ydata + index * trainer->ystep))
|
|
- trainer->f[index];
|
|
}
|
|
}
|
|
if( respnum > 0 )
|
|
{
|
|
/* rhat = median(y_i - F_(m-1)(x_i)) */
|
|
icvSort_32f( resp, respnum, 0 );
|
|
rhat = resp[respnum / 2];
|
|
|
|
/* val = sum{sign(r_i - rhat_i) * min(delta, abs(r_i - rhat_i)}
|
|
* r_i = y_i - F_(m-1)(x_i)
|
|
*/
|
|
val = 0.0F;
|
|
for( i = 0; i < respnum; i++ )
|
|
{
|
|
val += CV_SIGN( resp[i] - rhat )
|
|
* MIN( delta, (float) fabs( resp[i] - rhat ) );
|
|
}
|
|
|
|
val = rhat + val / (float) respnum;
|
|
}
|
|
else
|
|
{
|
|
val = 0.0F;
|
|
}
|
|
|
|
ptr->val[j] = val;
|
|
|
|
}
|
|
|
|
cvFree( &resid );
|
|
cvFree( &resp );
|
|
cvFree( &idx );
|
|
|
|
trees[0] = ptr;
|
|
}
|
|
|
|
//#define CV_VAL_MAX 1e304
|
|
|
|
//#define CV_LOG_VAL_MAX 700.0
|
|
|
|
#define CV_VAL_MAX 1e+8
|
|
|
|
#define CV_LOG_VAL_MAX 18.0
|
|
|
|
static void icvBtNext_L2CLASS( CvCARTClassifier** trees, CvBtTrainer* trainer )
|
|
{
|
|
CvCARTClassifier* ptr;
|
|
int i, j;
|
|
CvMat sample;
|
|
int sample_step;
|
|
uchar* sample_data;
|
|
|
|
int data_size;
|
|
int* idx;
|
|
int respnum;
|
|
float val;
|
|
double val_f;
|
|
|
|
float sum_weights;
|
|
float* weights;
|
|
float* sorted_weights;
|
|
CvMat* trimmed_idx;
|
|
CvMat* sample_idx;
|
|
int index;
|
|
int trimmed_num;
|
|
|
|
data_size = trainer->m * sizeof( *idx );
|
|
idx = (int*) cvAlloc( data_size );
|
|
|
|
data_size = trainer->m * sizeof( *weights );
|
|
weights = (float*) cvAlloc( data_size );
|
|
data_size = trainer->m * sizeof( *sorted_weights );
|
|
sorted_weights = (float*) cvAlloc( data_size );
|
|
|
|
/* yhat_i = (4 * y_i - 2) / ( 1 + exp( (4 * y_i - 2) * F_(m-1)(x_i) ) ).
|
|
* y_i in {0, 1}
|
|
*/
|
|
sum_weights = 0.0F;
|
|
for( i = 0; i < trainer->numsamples; i++ )
|
|
{
|
|
index = icvGetIdxAt( trainer->sampleIdx, i );
|
|
val = 4.0F * (*((float*) (trainer->ydata + index * trainer->ystep))) - 2.0F;
|
|
val_f = val * trainer->f[index];
|
|
val_f = ( val_f < CV_LOG_VAL_MAX ) ? exp( val_f ) : CV_LOG_VAL_MAX;
|
|
val = (float) ( (double) val / ( 1.0 + val_f ) );
|
|
trainer->y->data.fl[index] = val;
|
|
val = (float) fabs( val );
|
|
weights[index] = val * (2.0F - val);
|
|
sorted_weights[i] = weights[index];
|
|
sum_weights += sorted_weights[i];
|
|
}
|
|
|
|
trimmed_idx = NULL;
|
|
sample_idx = trainer->sampleIdx;
|
|
trimmed_num = trainer->numsamples;
|
|
if( trainer->param[1] < 1.0F )
|
|
{
|
|
/* perform weight trimming */
|
|
|
|
float threshold;
|
|
int count;
|
|
|
|
icvSort_32f( sorted_weights, trainer->numsamples, 0 );
|
|
|
|
sum_weights *= (1.0F - trainer->param[1]);
|
|
|
|
i = -1;
|
|
do { sum_weights -= sorted_weights[++i]; }
|
|
while( sum_weights > 0.0F && i < (trainer->numsamples - 1) );
|
|
|
|
threshold = sorted_weights[i];
|
|
|
|
while( i > 0 && sorted_weights[i-1] == threshold ) i--;
|
|
|
|
if( i > 0 )
|
|
{
|
|
trimmed_num = trainer->numsamples - i;
|
|
trimmed_idx = cvCreateMat( 1, trimmed_num, CV_32FC1 );
|
|
count = 0;
|
|
for( i = 0; i < trainer->numsamples; i++ )
|
|
{
|
|
index = icvGetIdxAt( trainer->sampleIdx, i );
|
|
if( weights[index] >= threshold )
|
|
{
|
|
CV_MAT_ELEM( *trimmed_idx, float, 0, count ) = (float) index;
|
|
count++;
|
|
}
|
|
}
|
|
|
|
assert( count == trimmed_num );
|
|
|
|
sample_idx = trimmed_idx;
|
|
|
|
printf( "Used samples %%: %g\n",
|
|
(float) trimmed_num / (float) trainer->numsamples * 100.0F );
|
|
}
|
|
}
|
|
|
|
ptr = (CvCARTClassifier*) cvCreateCARTClassifier( trainer->trainData, trainer->flags,
|
|
trainer->y, NULL, NULL, NULL, sample_idx, trainer->weights,
|
|
(CvClassifierTrainParams*) &trainer->cartParams );
|
|
|
|
CV_GET_SAMPLE( *trainer->trainData, trainer->flags, 0, sample );
|
|
CV_GET_SAMPLE_STEP( *trainer->trainData, trainer->flags, sample_step );
|
|
sample_data = sample.data.ptr;
|
|
for( i = 0; i < trimmed_num; i++ )
|
|
{
|
|
index = icvGetIdxAt( sample_idx, i );
|
|
sample.data.ptr = sample_data + index * sample_step;
|
|
idx[index] = (int) cvEvalCARTClassifierIdx( (CvClassifier*) ptr, &sample );
|
|
}
|
|
for( j = 0; j <= ptr->count; j++ )
|
|
{
|
|
respnum = 0;
|
|
val = 0.0F;
|
|
sum_weights = 0.0F;
|
|
for( i = 0; i < trimmed_num; i++ )
|
|
{
|
|
index = icvGetIdxAt( sample_idx, i );
|
|
if( idx[index] == j )
|
|
{
|
|
val += trainer->y->data.fl[index];
|
|
sum_weights += weights[index];
|
|
respnum++;
|
|
}
|
|
}
|
|
if( sum_weights > 0.0F )
|
|
{
|
|
val /= sum_weights;
|
|
}
|
|
else
|
|
{
|
|
val = 0.0F;
|
|
}
|
|
ptr->val[j] = val;
|
|
}
|
|
|
|
if( trimmed_idx != NULL ) cvReleaseMat( &trimmed_idx );
|
|
cvFree( &sorted_weights );
|
|
cvFree( &weights );
|
|
cvFree( &idx );
|
|
|
|
trees[0] = ptr;
|
|
}
|
|
|
|
static void icvBtNext_LKCLASS( CvCARTClassifier** trees, CvBtTrainer* trainer )
|
|
{
|
|
int i, j, k, kk, num;
|
|
CvMat sample;
|
|
int sample_step;
|
|
uchar* sample_data;
|
|
|
|
int data_size;
|
|
int* idx;
|
|
int respnum;
|
|
float val;
|
|
|
|
float sum_weights;
|
|
float* weights;
|
|
float* sorted_weights;
|
|
CvMat* trimmed_idx;
|
|
CvMat* sample_idx;
|
|
int index;
|
|
int trimmed_num;
|
|
double sum_exp_f;
|
|
double exp_f;
|
|
double f_k;
|
|
|
|
data_size = trainer->m * sizeof( *idx );
|
|
idx = (int*) cvAlloc( data_size );
|
|
data_size = trainer->m * sizeof( *weights );
|
|
weights = (float*) cvAlloc( data_size );
|
|
data_size = trainer->m * sizeof( *sorted_weights );
|
|
sorted_weights = (float*) cvAlloc( data_size );
|
|
trimmed_idx = cvCreateMat( 1, trainer->numsamples, CV_32FC1 );
|
|
|
|
for( k = 0; k < trainer->numclasses; k++ )
|
|
{
|
|
/* yhat_i = y_i - p_k(x_i), y_i in {0, 1} */
|
|
/* p_k(x_i) = exp(f_k(x_i)) / (sum_exp_f(x_i)) */
|
|
sum_weights = 0.0F;
|
|
for( i = 0; i < trainer->numsamples; i++ )
|
|
{
|
|
index = icvGetIdxAt( trainer->sampleIdx, i );
|
|
/* p_k(x_i) = 1 / (1 + sum(exp(f_kk(x_i) - f_k(x_i)))), kk != k */
|
|
num = index * trainer->numclasses;
|
|
f_k = (double) trainer->f[num + k];
|
|
sum_exp_f = 1.0;
|
|
for( kk = 0; kk < trainer->numclasses; kk++ )
|
|
{
|
|
if( kk == k ) continue;
|
|
exp_f = (double) trainer->f[num + kk] - f_k;
|
|
exp_f = (exp_f < CV_LOG_VAL_MAX) ? exp( exp_f ) : CV_VAL_MAX;
|
|
if( exp_f == CV_VAL_MAX || exp_f >= (CV_VAL_MAX - sum_exp_f) )
|
|
{
|
|
sum_exp_f = CV_VAL_MAX;
|
|
break;
|
|
}
|
|
sum_exp_f += exp_f;
|
|
}
|
|
|
|
val = (float) ( (*((float*) (trainer->ydata + index * trainer->ystep)))
|
|
== (float) k );
|
|
val -= (float) ( (sum_exp_f == CV_VAL_MAX) ? 0.0 : ( 1.0 / sum_exp_f ) );
|
|
|
|
assert( val >= -1.0F );
|
|
assert( val <= 1.0F );
|
|
|
|
trainer->y->data.fl[index] = val;
|
|
val = (float) fabs( val );
|
|
weights[index] = val * (1.0F - val);
|
|
sorted_weights[i] = weights[index];
|
|
sum_weights += sorted_weights[i];
|
|
}
|
|
|
|
sample_idx = trainer->sampleIdx;
|
|
trimmed_num = trainer->numsamples;
|
|
if( trainer->param[1] < 1.0F )
|
|
{
|
|
/* perform weight trimming */
|
|
|
|
float threshold;
|
|
int count;
|
|
|
|
icvSort_32f( sorted_weights, trainer->numsamples, 0 );
|
|
|
|
sum_weights *= (1.0F - trainer->param[1]);
|
|
|
|
i = -1;
|
|
do { sum_weights -= sorted_weights[++i]; }
|
|
while( sum_weights > 0.0F && i < (trainer->numsamples - 1) );
|
|
|
|
threshold = sorted_weights[i];
|
|
|
|
while( i > 0 && sorted_weights[i-1] == threshold ) i--;
|
|
|
|
if( i > 0 )
|
|
{
|
|
trimmed_num = trainer->numsamples - i;
|
|
trimmed_idx->cols = trimmed_num;
|
|
count = 0;
|
|
for( i = 0; i < trainer->numsamples; i++ )
|
|
{
|
|
index = icvGetIdxAt( trainer->sampleIdx, i );
|
|
if( weights[index] >= threshold )
|
|
{
|
|
CV_MAT_ELEM( *trimmed_idx, float, 0, count ) = (float) index;
|
|
count++;
|
|
}
|
|
}
|
|
|
|
assert( count == trimmed_num );
|
|
|
|
sample_idx = trimmed_idx;
|
|
|
|
printf( "k: %d Used samples %%: %g\n", k,
|
|
(float) trimmed_num / (float) trainer->numsamples * 100.0F );
|
|
}
|
|
} /* weight trimming */
|
|
|
|
trees[k] = (CvCARTClassifier*) cvCreateCARTClassifier( trainer->trainData,
|
|
trainer->flags, trainer->y, NULL, NULL, NULL, sample_idx, trainer->weights,
|
|
(CvClassifierTrainParams*) &trainer->cartParams );
|
|
|
|
CV_GET_SAMPLE( *trainer->trainData, trainer->flags, 0, sample );
|
|
CV_GET_SAMPLE_STEP( *trainer->trainData, trainer->flags, sample_step );
|
|
sample_data = sample.data.ptr;
|
|
for( i = 0; i < trimmed_num; i++ )
|
|
{
|
|
index = icvGetIdxAt( sample_idx, i );
|
|
sample.data.ptr = sample_data + index * sample_step;
|
|
idx[index] = (int) cvEvalCARTClassifierIdx( (CvClassifier*) trees[k],
|
|
&sample );
|
|
}
|
|
for( j = 0; j <= trees[k]->count; j++ )
|
|
{
|
|
respnum = 0;
|
|
val = 0.0F;
|
|
sum_weights = 0.0F;
|
|
for( i = 0; i < trimmed_num; i++ )
|
|
{
|
|
index = icvGetIdxAt( sample_idx, i );
|
|
if( idx[index] == j )
|
|
{
|
|
val += trainer->y->data.fl[index];
|
|
sum_weights += weights[index];
|
|
respnum++;
|
|
}
|
|
}
|
|
if( sum_weights > 0.0F )
|
|
{
|
|
val = ((float) (trainer->numclasses - 1)) * val /
|
|
((float) (trainer->numclasses)) / sum_weights;
|
|
}
|
|
else
|
|
{
|
|
val = 0.0F;
|
|
}
|
|
trees[k]->val[j] = val;
|
|
}
|
|
} /* for each class */
|
|
|
|
cvReleaseMat( &trimmed_idx );
|
|
cvFree( &sorted_weights );
|
|
cvFree( &weights );
|
|
cvFree( &idx );
|
|
}
|
|
|
|
|
|
static void icvBtNext_XXBCLASS( CvCARTClassifier** trees, CvBtTrainer* trainer )
|
|
{
|
|
float alpha;
|
|
int i;
|
|
CvMat* weak_eval_vals;
|
|
CvMat* sample_idx;
|
|
int num_samples;
|
|
CvMat sample;
|
|
uchar* sample_data;
|
|
int sample_step;
|
|
|
|
weak_eval_vals = cvCreateMat( 1, trainer->m, CV_32FC1 );
|
|
|
|
sample_idx = cvTrimWeights( trainer->weights, trainer->sampleIdx,
|
|
trainer->param[1] );
|
|
num_samples = ( sample_idx == NULL )
|
|
? trainer->m : MAX( sample_idx->rows, sample_idx->cols );
|
|
|
|
printf( "Used samples %%: %g\n",
|
|
(float) num_samples / (float) trainer->numsamples * 100.0F );
|
|
|
|
trees[0] = (CvCARTClassifier*) cvCreateCARTClassifier( trainer->trainData,
|
|
trainer->flags, trainer->y, NULL, NULL, NULL,
|
|
sample_idx, trainer->weights,
|
|
(CvClassifierTrainParams*) &trainer->cartParams );
|
|
|
|
/* evaluate samples */
|
|
CV_GET_SAMPLE( *trainer->trainData, trainer->flags, 0, sample );
|
|
CV_GET_SAMPLE_STEP( *trainer->trainData, trainer->flags, sample_step );
|
|
sample_data = sample.data.ptr;
|
|
|
|
for( i = 0; i < trainer->m; i++ )
|
|
{
|
|
sample.data.ptr = sample_data + i * sample_step;
|
|
weak_eval_vals->data.fl[i] = trees[0]->eval( (CvClassifier*) trees[0], &sample );
|
|
}
|
|
|
|
alpha = cvBoostNextWeakClassifier( weak_eval_vals, trainer->trainClasses,
|
|
trainer->y, trainer->weights, trainer->boosttrainer );
|
|
|
|
/* multiply tree by alpha */
|
|
for( i = 0; i <= trees[0]->count; i++ )
|
|
{
|
|
trees[0]->val[i] *= alpha;
|
|
}
|
|
if( trainer->type == CV_RABCLASS )
|
|
{
|
|
for( i = 0; i <= trees[0]->count; i++ )
|
|
{
|
|
trees[0]->val[i] = cvLogRatio( trees[0]->val[i] );
|
|
}
|
|
}
|
|
|
|
if( sample_idx != NULL && sample_idx != trainer->sampleIdx )
|
|
{
|
|
cvReleaseMat( &sample_idx );
|
|
}
|
|
cvReleaseMat( &weak_eval_vals );
|
|
}
|
|
|
|
typedef void (*CvBtNextFunc)( CvCARTClassifier** trees, CvBtTrainer* trainer );
|
|
|
|
static CvBtNextFunc icvBtNextFunc[] =
|
|
{
|
|
icvBtNext_XXBCLASS,
|
|
icvBtNext_XXBCLASS,
|
|
icvBtNext_XXBCLASS,
|
|
icvBtNext_XXBCLASS,
|
|
icvBtNext_L2CLASS,
|
|
icvBtNext_LKCLASS,
|
|
icvBtNext_LSREG,
|
|
icvBtNext_LADREG,
|
|
icvBtNext_MREG
|
|
};
|
|
|
|
CV_BOOST_IMPL
|
|
void cvBtNext( CvCARTClassifier** trees, CvBtTrainer* trainer )
|
|
{
|
|
int i, j;
|
|
int index;
|
|
CvMat sample;
|
|
int sample_step;
|
|
uchar* sample_data;
|
|
|
|
icvBtNextFunc[trainer->type]( trees, trainer );
|
|
|
|
/* shrinkage */
|
|
if( trainer->param[0] != 1.0F )
|
|
{
|
|
for( j = 0; j < trainer->numclasses; j++ )
|
|
{
|
|
for( i = 0; i <= trees[j]->count; i++ )
|
|
{
|
|
trees[j]->val[i] *= trainer->param[0];
|
|
}
|
|
}
|
|
}
|
|
|
|
if( trainer->type > CV_GABCLASS )
|
|
{
|
|
/* update F_(m-1) */
|
|
CV_GET_SAMPLE( *(trainer->trainData), trainer->flags, 0, sample );
|
|
CV_GET_SAMPLE_STEP( *(trainer->trainData), trainer->flags, sample_step );
|
|
sample_data = sample.data.ptr;
|
|
for( i = 0; i < trainer->numsamples; i++ )
|
|
{
|
|
index = icvGetIdxAt( trainer->sampleIdx, i );
|
|
sample.data.ptr = sample_data + index * sample_step;
|
|
for( j = 0; j < trainer->numclasses; j++ )
|
|
{
|
|
trainer->f[index * trainer->numclasses + j] +=
|
|
trees[j]->eval( (CvClassifier*) (trees[j]), &sample );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static
|
|
void cvBtEnd( CvBtTrainer** trainer )
|
|
{
|
|
CV_FUNCNAME( "cvBtEnd" );
|
|
|
|
__BEGIN__;
|
|
|
|
if( trainer == NULL || (*trainer) == NULL )
|
|
{
|
|
CV_ERROR( CV_StsNullPtr, "Invalid trainer parameter" );
|
|
}
|
|
|
|
if( (*trainer)->y != NULL )
|
|
{
|
|
CV_CALL( cvReleaseMat( &((*trainer)->y) ) );
|
|
}
|
|
if( (*trainer)->weights != NULL )
|
|
{
|
|
CV_CALL( cvReleaseMat( &((*trainer)->weights) ) );
|
|
}
|
|
if( (*trainer)->boosttrainer != NULL )
|
|
{
|
|
CV_CALL( cvBoostEndTraining( &((*trainer)->boosttrainer) ) );
|
|
}
|
|
CV_CALL( cvFree( trainer ) );
|
|
|
|
__END__;
|
|
}
|
|
|
|
/****************************************************************************************\
|
|
* Boosted tree model as a classifier *
|
|
\****************************************************************************************/
|
|
|
|
static
|
|
float cvEvalBtClassifier( CvClassifier* classifier, CvMat* sample )
|
|
{
|
|
float val;
|
|
|
|
CV_FUNCNAME( "cvEvalBtClassifier" );
|
|
|
|
__BEGIN__;
|
|
|
|
int i;
|
|
|
|
val = 0.0F;
|
|
if( CV_IS_TUNABLE( classifier->flags ) )
|
|
{
|
|
CvSeqReader reader;
|
|
CvCARTClassifier* tree;
|
|
|
|
CV_CALL( cvStartReadSeq( ((CvBtClassifier*) classifier)->seq, &reader ) );
|
|
for( i = 0; i < ((CvBtClassifier*) classifier)->numiter; i++ )
|
|
{
|
|
CV_READ_SEQ_ELEM( tree, reader );
|
|
val += tree->eval( (CvClassifier*) tree, sample );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CvCARTClassifier** ptree;
|
|
|
|
ptree = ((CvBtClassifier*) classifier)->trees;
|
|
for( i = 0; i < ((CvBtClassifier*) classifier)->numiter; i++ )
|
|
{
|
|
val += (*ptree)->eval( (CvClassifier*) (*ptree), sample );
|
|
ptree++;
|
|
}
|
|
}
|
|
|
|
__END__;
|
|
|
|
return val;
|
|
}
|
|
|
|
static
|
|
float cvEvalBtClassifier2( CvClassifier* classifier, CvMat* sample )
|
|
{
|
|
float val;
|
|
|
|
CV_FUNCNAME( "cvEvalBtClassifier2" );
|
|
|
|
__BEGIN__;
|
|
|
|
CV_CALL( val = cvEvalBtClassifier( classifier, sample ) );
|
|
|
|
__END__;
|
|
|
|
return (float) (val >= 0.0F);
|
|
}
|
|
|
|
static
|
|
float cvEvalBtClassifierK( CvClassifier* classifier, CvMat* sample )
|
|
{
|
|
int cls = 0;
|
|
|
|
CV_FUNCNAME( "cvEvalBtClassifierK" );
|
|
|
|
__BEGIN__;
|
|
|
|
int i, k;
|
|
float max_val;
|
|
int numclasses;
|
|
|
|
float* vals;
|
|
size_t data_size;
|
|
|
|
numclasses = ((CvBtClassifier*) classifier)->numclasses;
|
|
data_size = sizeof( *vals ) * numclasses;
|
|
CV_CALL( vals = (float*) cvAlloc( data_size ) );
|
|
memset( vals, 0, data_size );
|
|
|
|
if( CV_IS_TUNABLE( classifier->flags ) )
|
|
{
|
|
CvSeqReader reader;
|
|
CvCARTClassifier* tree;
|
|
|
|
CV_CALL( cvStartReadSeq( ((CvBtClassifier*) classifier)->seq, &reader ) );
|
|
for( i = 0; i < ((CvBtClassifier*) classifier)->numiter; i++ )
|
|
{
|
|
for( k = 0; k < numclasses; k++ )
|
|
{
|
|
CV_READ_SEQ_ELEM( tree, reader );
|
|
vals[k] += tree->eval( (CvClassifier*) tree, sample );
|
|
}
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
CvCARTClassifier** ptree;
|
|
|
|
ptree = ((CvBtClassifier*) classifier)->trees;
|
|
for( i = 0; i < ((CvBtClassifier*) classifier)->numiter; i++ )
|
|
{
|
|
for( k = 0; k < numclasses; k++ )
|
|
{
|
|
vals[k] += (*ptree)->eval( (CvClassifier*) (*ptree), sample );
|
|
ptree++;
|
|
}
|
|
}
|
|
}
|
|
|
|
max_val = vals[cls];
|
|
for( k = 1; k < numclasses; k++ )
|
|
{
|
|
if( vals[k] > max_val )
|
|
{
|
|
max_val = vals[k];
|
|
cls = k;
|
|
}
|
|
}
|
|
|
|
CV_CALL( cvFree( &vals ) );
|
|
|
|
__END__;
|
|
|
|
return (float) cls;
|
|
}
|
|
|
|
typedef float (*CvEvalBtClassifier)( CvClassifier* classifier, CvMat* sample );
|
|
|
|
static CvEvalBtClassifier icvEvalBtClassifier[] =
|
|
{
|
|
cvEvalBtClassifier2,
|
|
cvEvalBtClassifier2,
|
|
cvEvalBtClassifier2,
|
|
cvEvalBtClassifier2,
|
|
cvEvalBtClassifier2,
|
|
cvEvalBtClassifierK,
|
|
cvEvalBtClassifier,
|
|
cvEvalBtClassifier,
|
|
cvEvalBtClassifier
|
|
};
|
|
|
|
static
|
|
int cvSaveBtClassifier( CvClassifier* classifier, const char* filename )
|
|
{
|
|
CV_FUNCNAME( "cvSaveBtClassifier" );
|
|
|
|
__BEGIN__;
|
|
|
|
FILE* file;
|
|
int i, j;
|
|
CvSeqReader reader;
|
|
memset(&reader, 0, sizeof(reader));
|
|
CvCARTClassifier* tree;
|
|
|
|
CV_ASSERT( classifier );
|
|
CV_ASSERT( filename );
|
|
|
|
if( !icvMkDir( filename ) || (file = fopen( filename, "w" )) == 0 )
|
|
{
|
|
CV_ERROR( CV_StsError, "Unable to create file" );
|
|
}
|
|
|
|
if( CV_IS_TUNABLE( classifier->flags ) )
|
|
{
|
|
CV_CALL( cvStartReadSeq( ((CvBtClassifier*) classifier)->seq, &reader ) );
|
|
}
|
|
fprintf( file, "%d %d\n%d\n%d\n", (int) ((CvBtClassifier*) classifier)->type,
|
|
((CvBtClassifier*) classifier)->numclasses,
|
|
((CvBtClassifier*) classifier)->numfeatures,
|
|
((CvBtClassifier*) classifier)->numiter );
|
|
|
|
for( i = 0; i < ((CvBtClassifier*) classifier)->numclasses *
|
|
((CvBtClassifier*) classifier)->numiter; i++ )
|
|
{
|
|
if( CV_IS_TUNABLE( classifier->flags ) )
|
|
{
|
|
CV_READ_SEQ_ELEM( tree, reader );
|
|
}
|
|
else
|
|
{
|
|
tree = ((CvBtClassifier*) classifier)->trees[i];
|
|
}
|
|
|
|
fprintf( file, "%d\n", tree->count );
|
|
for( j = 0; j < tree->count; j++ )
|
|
{
|
|
fprintf( file, "%d %g %d %d\n", tree->compidx[j],
|
|
tree->threshold[j],
|
|
tree->left[j],
|
|
tree->right[j] );
|
|
}
|
|
for( j = 0; j <= tree->count; j++ )
|
|
{
|
|
fprintf( file, "%g ", tree->val[j] );
|
|
}
|
|
fprintf( file, "\n" );
|
|
}
|
|
|
|
fclose( file );
|
|
|
|
__END__;
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
static
|
|
void cvReleaseBtClassifier( CvClassifier** ptr )
|
|
{
|
|
CV_FUNCNAME( "cvReleaseBtClassifier" );
|
|
|
|
__BEGIN__;
|
|
|
|
int i;
|
|
|
|
if( ptr == NULL || *ptr == NULL )
|
|
{
|
|
CV_ERROR( CV_StsNullPtr, "" );
|
|
}
|
|
if( CV_IS_TUNABLE( (*ptr)->flags ) )
|
|
{
|
|
CvSeqReader reader;
|
|
CvCARTClassifier* tree;
|
|
|
|
CV_CALL( cvStartReadSeq( ((CvBtClassifier*) *ptr)->seq, &reader ) );
|
|
for( i = 0; i < ((CvBtClassifier*) *ptr)->numclasses *
|
|
((CvBtClassifier*) *ptr)->numiter; i++ )
|
|
{
|
|
CV_READ_SEQ_ELEM( tree, reader );
|
|
tree->release( (CvClassifier**) (&tree) );
|
|
}
|
|
CV_CALL( cvReleaseMemStorage( &(((CvBtClassifier*) *ptr)->seq->storage) ) );
|
|
}
|
|
else
|
|
{
|
|
CvCARTClassifier** ptree;
|
|
|
|
ptree = ((CvBtClassifier*) *ptr)->trees;
|
|
for( i = 0; i < ((CvBtClassifier*) *ptr)->numclasses *
|
|
((CvBtClassifier*) *ptr)->numiter; i++ )
|
|
{
|
|
(*ptree)->release( (CvClassifier**) ptree );
|
|
ptree++;
|
|
}
|
|
}
|
|
|
|
CV_CALL( cvFree( ptr ) );
|
|
*ptr = NULL;
|
|
|
|
__END__;
|
|
}
|
|
|
|
static void cvTuneBtClassifier( CvClassifier* classifier, CvMat*, int flags,
|
|
CvMat*, CvMat* , CvMat*, CvMat*, CvMat* )
|
|
{
|
|
CV_FUNCNAME( "cvTuneBtClassifier" );
|
|
|
|
__BEGIN__;
|
|
|
|
size_t data_size;
|
|
|
|
if( CV_IS_TUNABLE( flags ) )
|
|
{
|
|
if( !CV_IS_TUNABLE( classifier->flags ) )
|
|
{
|
|
CV_ERROR( CV_StsUnsupportedFormat,
|
|
"Classifier does not support tune function" );
|
|
}
|
|
else
|
|
{
|
|
/* tune classifier */
|
|
CvCARTClassifier** trees;
|
|
|
|
printf( "Iteration %d\n", ((CvBtClassifier*) classifier)->numiter + 1 );
|
|
|
|
data_size = sizeof( *trees ) * ((CvBtClassifier*) classifier)->numclasses;
|
|
CV_CALL( trees = (CvCARTClassifier**) cvAlloc( data_size ) );
|
|
CV_CALL( cvBtNext( trees,
|
|
(CvBtTrainer*) ((CvBtClassifier*) classifier)->trainer ) );
|
|
CV_CALL( cvSeqPushMulti( ((CvBtClassifier*) classifier)->seq,
|
|
trees, ((CvBtClassifier*) classifier)->numclasses ) );
|
|
CV_CALL( cvFree( &trees ) );
|
|
((CvBtClassifier*) classifier)->numiter++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( CV_IS_TUNABLE( classifier->flags ) )
|
|
{
|
|
/* convert */
|
|
void* ptr;
|
|
|
|
assert( ((CvBtClassifier*) classifier)->seq->total ==
|
|
((CvBtClassifier*) classifier)->numiter *
|
|
((CvBtClassifier*) classifier)->numclasses );
|
|
|
|
data_size = sizeof( ((CvBtClassifier*) classifier)->trees[0] ) *
|
|
((CvBtClassifier*) classifier)->seq->total;
|
|
CV_CALL( ptr = cvAlloc( data_size ) );
|
|
CV_CALL( cvCvtSeqToArray( ((CvBtClassifier*) classifier)->seq, ptr ) );
|
|
CV_CALL( cvReleaseMemStorage(
|
|
&(((CvBtClassifier*) classifier)->seq->storage) ) );
|
|
((CvBtClassifier*) classifier)->trees = (CvCARTClassifier**) ptr;
|
|
classifier->flags &= ~CV_TUNABLE;
|
|
CV_CALL( cvBtEnd( (CvBtTrainer**)
|
|
&(((CvBtClassifier*) classifier)->trainer )) );
|
|
((CvBtClassifier*) classifier)->trainer = NULL;
|
|
}
|
|
}
|
|
|
|
__END__;
|
|
}
|
|
|
|
static CvBtClassifier* icvAllocBtClassifier( CvBoostType type, int flags, int numclasses,
|
|
int numiter )
|
|
{
|
|
CvBtClassifier* ptr;
|
|
size_t data_size;
|
|
|
|
assert( numclasses >= 1 );
|
|
assert( numiter >= 0 );
|
|
assert( ( numclasses == 1 ) || (type == CV_LKCLASS) );
|
|
|
|
data_size = sizeof( *ptr );
|
|
ptr = (CvBtClassifier*) cvAlloc( data_size );
|
|
memset( ptr, 0, data_size );
|
|
|
|
if( CV_IS_TUNABLE( flags ) )
|
|
{
|
|
ptr->seq = cvCreateSeq( 0, sizeof( *(ptr->seq) ), sizeof( *(ptr->trees) ),
|
|
cvCreateMemStorage() );
|
|
ptr->numiter = 0;
|
|
}
|
|
else
|
|
{
|
|
data_size = numclasses * numiter * sizeof( *(ptr->trees) );
|
|
ptr->trees = (CvCARTClassifier**) cvAlloc( data_size );
|
|
memset( ptr->trees, 0, data_size );
|
|
|
|
ptr->numiter = numiter;
|
|
}
|
|
|
|
ptr->flags = flags;
|
|
ptr->numclasses = numclasses;
|
|
ptr->type = type;
|
|
|
|
ptr->eval = icvEvalBtClassifier[(int) type];
|
|
ptr->tune = cvTuneBtClassifier;
|
|
ptr->save = cvSaveBtClassifier;
|
|
ptr->release = cvReleaseBtClassifier;
|
|
|
|
return ptr;
|
|
}
|
|
|
|
CV_BOOST_IMPL
|
|
CvClassifier* cvCreateBtClassifier( CvMat* trainData,
|
|
int flags,
|
|
CvMat* trainClasses,
|
|
CvMat* typeMask,
|
|
CvMat* missedMeasurementsMask,
|
|
CvMat* compIdx,
|
|
CvMat* sampleIdx,
|
|
CvMat* weights,
|
|
CvClassifierTrainParams* trainParams )
|
|
{
|
|
CvBtClassifier* ptr = 0;
|
|
|
|
CV_FUNCNAME( "cvCreateBtClassifier" );
|
|
|
|
__BEGIN__;
|
|
CvBoostType type;
|
|
int num_classes;
|
|
int num_iter;
|
|
int i;
|
|
CvCARTClassifier** trees;
|
|
size_t data_size;
|
|
|
|
CV_ASSERT( trainData != NULL );
|
|
CV_ASSERT( trainClasses != NULL );
|
|
CV_ASSERT( typeMask == NULL );
|
|
CV_ASSERT( missedMeasurementsMask == NULL );
|
|
CV_ASSERT( compIdx == NULL );
|
|
CV_ASSERT( weights == NULL );
|
|
CV_ASSERT( trainParams != NULL );
|
|
|
|
type = ((CvBtClassifierTrainParams*) trainParams)->type;
|
|
|
|
if( type >= CV_DABCLASS && type <= CV_GABCLASS && sampleIdx )
|
|
{
|
|
CV_ERROR( CV_StsBadArg, "Sample indices are not supported for this type" );
|
|
}
|
|
|
|
if( type == CV_LKCLASS )
|
|
{
|
|
double min_val;
|
|
double max_val;
|
|
|
|
cvMinMaxLoc( trainClasses, &min_val, &max_val );
|
|
num_classes = (int) (max_val + 1.0);
|
|
|
|
CV_ASSERT( num_classes >= 2 );
|
|
}
|
|
else
|
|
{
|
|
num_classes = 1;
|
|
}
|
|
num_iter = ((CvBtClassifierTrainParams*) trainParams)->numiter;
|
|
|
|
CV_ASSERT( num_iter > 0 );
|
|
|
|
ptr = icvAllocBtClassifier( type, CV_TUNABLE | flags, num_classes, num_iter );
|
|
ptr->numfeatures = (CV_IS_ROW_SAMPLE( flags )) ? trainData->cols : trainData->rows;
|
|
|
|
i = 0;
|
|
|
|
printf( "Iteration %d\n", 1 );
|
|
|
|
data_size = sizeof( *trees ) * ptr->numclasses;
|
|
CV_CALL( trees = (CvCARTClassifier**) cvAlloc( data_size ) );
|
|
|
|
CV_CALL( ptr->trainer = cvBtStart( trees, trainData, flags, trainClasses, sampleIdx,
|
|
((CvBtClassifierTrainParams*) trainParams)->numsplits, type, num_classes,
|
|
&(((CvBtClassifierTrainParams*) trainParams)->param[0]) ) );
|
|
|
|
CV_CALL( cvSeqPushMulti( ptr->seq, trees, ptr->numclasses ) );
|
|
CV_CALL( cvFree( &trees ) );
|
|
ptr->numiter++;
|
|
|
|
for( i = 1; i < num_iter; i++ )
|
|
{
|
|
ptr->tune( (CvClassifier*) ptr, NULL, CV_TUNABLE, NULL, NULL, NULL, NULL, NULL );
|
|
}
|
|
if( !CV_IS_TUNABLE( flags ) )
|
|
{
|
|
/* convert */
|
|
ptr->tune( (CvClassifier*) ptr, NULL, 0, NULL, NULL, NULL, NULL, NULL );
|
|
}
|
|
|
|
__END__;
|
|
|
|
return (CvClassifier*) ptr;
|
|
}
|
|
|
|
CV_BOOST_IMPL
|
|
CvClassifier* cvCreateBtClassifierFromFile( const char* filename )
|
|
{
|
|
CvBtClassifier* ptr = 0;
|
|
|
|
CV_FUNCNAME( "cvCreateBtClassifierFromFile" );
|
|
|
|
__BEGIN__;
|
|
|
|
FILE* file;
|
|
int i, j;
|
|
int data_size;
|
|
int num_classifiers;
|
|
int num_features;
|
|
int num_classes;
|
|
int type;
|
|
int values_read = -1;
|
|
|
|
CV_ASSERT( filename != NULL );
|
|
|
|
ptr = NULL;
|
|
file = fopen( filename, "r" );
|
|
if( !file )
|
|
{
|
|
CV_ERROR( CV_StsError, "Unable to open file" );
|
|
}
|
|
|
|
values_read = fscanf( file, "%d %d %d %d", &type, &num_classes, &num_features, &num_classifiers );
|
|
CV_Assert(values_read == 4);
|
|
|
|
CV_ASSERT( type >= (int) CV_DABCLASS && type <= (int) CV_MREG );
|
|
CV_ASSERT( num_features > 0 );
|
|
CV_ASSERT( num_classifiers > 0 );
|
|
|
|
if( (CvBoostType) type != CV_LKCLASS )
|
|
{
|
|
num_classes = 1;
|
|
}
|
|
ptr = icvAllocBtClassifier( (CvBoostType) type, 0, num_classes, num_classifiers );
|
|
ptr->numfeatures = num_features;
|
|
|
|
for( i = 0; i < num_classes * num_classifiers; i++ )
|
|
{
|
|
int count;
|
|
CvCARTClassifier* tree;
|
|
|
|
values_read = fscanf( file, "%d", &count );
|
|
CV_Assert(values_read == 1);
|
|
|
|
data_size = sizeof( *tree )
|
|
+ count * ( sizeof( *(tree->compidx) ) + sizeof( *(tree->threshold) ) +
|
|
sizeof( *(tree->right) ) + sizeof( *(tree->left) ) )
|
|
+ (count + 1) * ( sizeof( *(tree->val) ) );
|
|
CV_CALL( tree = (CvCARTClassifier*) cvAlloc( data_size ) );
|
|
memset( tree, 0, data_size );
|
|
tree->eval = cvEvalCARTClassifier;
|
|
tree->tune = NULL;
|
|
tree->save = NULL;
|
|
tree->release = cvReleaseCARTClassifier;
|
|
tree->compidx = (int*) ( tree + 1 );
|
|
tree->threshold = (float*) ( tree->compidx + count );
|
|
tree->left = (int*) ( tree->threshold + count );
|
|
tree->right = (int*) ( tree->left + count );
|
|
tree->val = (float*) ( tree->right + count );
|
|
|
|
tree->count = count;
|
|
for( j = 0; j < tree->count; j++ )
|
|
{
|
|
values_read = fscanf( file, "%d %g %d %d", &(tree->compidx[j]),
|
|
&(tree->threshold[j]),
|
|
&(tree->left[j]),
|
|
&(tree->right[j]) );
|
|
CV_Assert(values_read == 4);
|
|
}
|
|
for( j = 0; j <= tree->count; j++ )
|
|
{
|
|
values_read = fscanf( file, "%g", &(tree->val[j]) );
|
|
CV_Assert(values_read == 1);
|
|
}
|
|
ptr->trees[i] = tree;
|
|
}
|
|
|
|
fclose( file );
|
|
|
|
__END__;
|
|
|
|
return (CvClassifier*) ptr;
|
|
}
|
|
|
|
/****************************************************************************************\
|
|
* Utility functions *
|
|
\****************************************************************************************/
|
|
|
|
CV_BOOST_IMPL
|
|
CvMat* cvTrimWeights( CvMat* weights, CvMat* idx, float factor )
|
|
{
|
|
CvMat* ptr = 0;
|
|
|
|
CV_FUNCNAME( "cvTrimWeights" );
|
|
__BEGIN__;
|
|
int i, index, num;
|
|
float sum_weights;
|
|
uchar* wdata;
|
|
size_t wstep;
|
|
int wnum;
|
|
float threshold;
|
|
int count;
|
|
float* sorted_weights;
|
|
|
|
CV_ASSERT( CV_MAT_TYPE( weights->type ) == CV_32FC1 );
|
|
|
|
ptr = idx;
|
|
sorted_weights = NULL;
|
|
|
|
if( factor > 0.0F && factor < 1.0F )
|
|
{
|
|
size_t data_size;
|
|
|
|
CV_MAT2VEC( *weights, wdata, wstep, wnum );
|
|
num = ( idx == NULL ) ? wnum : MAX( idx->rows, idx->cols );
|
|
|
|
data_size = num * sizeof( *sorted_weights );
|
|
sorted_weights = (float*) cvAlloc( data_size );
|
|
memset( sorted_weights, 0, data_size );
|
|
|
|
sum_weights = 0.0F;
|
|
for( i = 0; i < num; i++ )
|
|
{
|
|
index = icvGetIdxAt( idx, i );
|
|
sorted_weights[i] = *((float*) (wdata + index * wstep));
|
|
sum_weights += sorted_weights[i];
|
|
}
|
|
|
|
icvSort_32f( sorted_weights, num, 0 );
|
|
|
|
sum_weights *= (1.0F - factor);
|
|
|
|
i = -1;
|
|
do { sum_weights -= sorted_weights[++i]; }
|
|
while( sum_weights > 0.0F && i < (num - 1) );
|
|
|
|
threshold = sorted_weights[i];
|
|
|
|
while( i > 0 && sorted_weights[i-1] == threshold ) i--;
|
|
|
|
if( i > 0 || ( idx != NULL && CV_MAT_TYPE( idx->type ) != CV_32FC1 ) )
|
|
{
|
|
CV_CALL( ptr = cvCreateMat( 1, num - i, CV_32FC1 ) );
|
|
count = 0;
|
|
for( i = 0; i < num; i++ )
|
|
{
|
|
index = icvGetIdxAt( idx, i );
|
|
if( *((float*) (wdata + index * wstep)) >= threshold )
|
|
{
|
|
CV_MAT_ELEM( *ptr, float, 0, count ) = (float) index;
|
|
count++;
|
|
}
|
|
}
|
|
|
|
assert( count == ptr->cols );
|
|
}
|
|
cvFree( &sorted_weights );
|
|
}
|
|
|
|
__END__;
|
|
|
|
return ptr;
|
|
}
|
|
|
|
|
|
CV_BOOST_IMPL
|
|
void cvReadTrainData( const char* filename, int flags,
|
|
CvMat** trainData,
|
|
CvMat** trainClasses )
|
|
{
|
|
|
|
CV_FUNCNAME( "cvReadTrainData" );
|
|
|
|
__BEGIN__;
|
|
|
|
FILE* file;
|
|
int m, n;
|
|
int i, j;
|
|
float val;
|
|
int values_read = -1;
|
|
|
|
if( filename == NULL )
|
|
{
|
|
CV_ERROR( CV_StsNullPtr, "filename must be specified" );
|
|
}
|
|
if( trainData == NULL )
|
|
{
|
|
CV_ERROR( CV_StsNullPtr, "trainData must be not NULL" );
|
|
}
|
|
if( trainClasses == NULL )
|
|
{
|
|
CV_ERROR( CV_StsNullPtr, "trainClasses must be not NULL" );
|
|
}
|
|
|
|
*trainData = NULL;
|
|
*trainClasses = NULL;
|
|
file = fopen( filename, "r" );
|
|
if( !file )
|
|
{
|
|
CV_ERROR( CV_StsError, "Unable to open file" );
|
|
}
|
|
|
|
values_read = fscanf( file, "%d %d", &m, &n );
|
|
CV_Assert(values_read == 2);
|
|
|
|
if( CV_IS_ROW_SAMPLE( flags ) )
|
|
{
|
|
CV_CALL( *trainData = cvCreateMat( m, n, CV_32FC1 ) );
|
|
}
|
|
else
|
|
{
|
|
CV_CALL( *trainData = cvCreateMat( n, m, CV_32FC1 ) );
|
|
}
|
|
|
|
CV_CALL( *trainClasses = cvCreateMat( 1, m, CV_32FC1 ) );
|
|
|
|
for( i = 0; i < m; i++ )
|
|
{
|
|
for( j = 0; j < n; j++ )
|
|
{
|
|
values_read = fscanf( file, "%f", &val );
|
|
CV_Assert(values_read == 1);
|
|
if( CV_IS_ROW_SAMPLE( flags ) )
|
|
{
|
|
CV_MAT_ELEM( **trainData, float, i, j ) = val;
|
|
}
|
|
else
|
|
{
|
|
CV_MAT_ELEM( **trainData, float, j, i ) = val;
|
|
}
|
|
}
|
|
values_read = fscanf( file, "%f", &val );
|
|
CV_Assert(values_read == 2);
|
|
CV_MAT_ELEM( **trainClasses, float, 0, i ) = val;
|
|
}
|
|
|
|
fclose( file );
|
|
|
|
__END__;
|
|
|
|
}
|
|
|
|
CV_BOOST_IMPL
|
|
void cvWriteTrainData( const char* filename, int flags,
|
|
CvMat* trainData, CvMat* trainClasses, CvMat* sampleIdx )
|
|
{
|
|
CV_FUNCNAME( "cvWriteTrainData" );
|
|
|
|
__BEGIN__;
|
|
|
|
FILE* file;
|
|
int m, n;
|
|
int i, j;
|
|
int clsrow;
|
|
int count;
|
|
int idx;
|
|
CvScalar sc;
|
|
|
|
if( filename == NULL )
|
|
{
|
|
CV_ERROR( CV_StsNullPtr, "filename must be specified" );
|
|
}
|
|
if( trainData == NULL || CV_MAT_TYPE( trainData->type ) != CV_32FC1 )
|
|
{
|
|
CV_ERROR( CV_StsUnsupportedFormat, "Invalid trainData" );
|
|
}
|
|
if( CV_IS_ROW_SAMPLE( flags ) )
|
|
{
|
|
m = trainData->rows;
|
|
n = trainData->cols;
|
|
}
|
|
else
|
|
{
|
|
n = trainData->rows;
|
|
m = trainData->cols;
|
|
}
|
|
if( trainClasses == NULL || CV_MAT_TYPE( trainClasses->type ) != CV_32FC1 ||
|
|
MIN( trainClasses->rows, trainClasses->cols ) != 1 )
|
|
{
|
|
CV_ERROR( CV_StsUnsupportedFormat, "Invalid trainClasses" );
|
|
}
|
|
clsrow = (trainClasses->rows == 1);
|
|
if( m != ( (clsrow) ? trainClasses->cols : trainClasses->rows ) )
|
|
{
|
|
CV_ERROR( CV_StsUnmatchedSizes, "Incorrect trainData and trainClasses sizes" );
|
|
}
|
|
|
|
if( sampleIdx != NULL )
|
|
{
|
|
count = (sampleIdx->rows == 1) ? sampleIdx->cols : sampleIdx->rows;
|
|
}
|
|
else
|
|
{
|
|
count = m;
|
|
}
|
|
|
|
|
|
file = fopen( filename, "w" );
|
|
if( !file )
|
|
{
|
|
CV_ERROR( CV_StsError, "Unable to create file" );
|
|
}
|
|
|
|
fprintf( file, "%d %d\n", count, n );
|
|
|
|
for( i = 0; i < count; i++ )
|
|
{
|
|
if( sampleIdx )
|
|
{
|
|
if( sampleIdx->rows == 1 )
|
|
{
|
|
sc = cvGet2D( sampleIdx, 0, i );
|
|
}
|
|
else
|
|
{
|
|
sc = cvGet2D( sampleIdx, i, 0 );
|
|
}
|
|
idx = (int) sc.val[0];
|
|
}
|
|
else
|
|
{
|
|
idx = i;
|
|
}
|
|
for( j = 0; j < n; j++ )
|
|
{
|
|
fprintf( file, "%g ", ( (CV_IS_ROW_SAMPLE( flags ))
|
|
? CV_MAT_ELEM( *trainData, float, idx, j )
|
|
: CV_MAT_ELEM( *trainData, float, j, idx ) ) );
|
|
}
|
|
fprintf( file, "%g\n", ( (clsrow)
|
|
? CV_MAT_ELEM( *trainClasses, float, 0, idx )
|
|
: CV_MAT_ELEM( *trainClasses, float, idx, 0 ) ) );
|
|
}
|
|
|
|
fclose( file );
|
|
|
|
__END__;
|
|
}
|
|
|
|
|
|
#define ICV_RAND_SHUFFLE( suffix, type ) \
|
|
static void icvRandShuffle_##suffix( uchar* data, size_t step, int num ) \
|
|
{ \
|
|
time_t seed; \
|
|
type tmp; \
|
|
int i; \
|
|
float rn; \
|
|
\
|
|
time( &seed ); \
|
|
CvRNG state = cvRNG((int)seed); \
|
|
\
|
|
for( i = 0; i < (num-1); i++ ) \
|
|
{ \
|
|
rn = ((float) cvRandInt( &state )) / (1.0F + UINT_MAX); \
|
|
CV_SWAP( *((type*)(data + i * step)), \
|
|
*((type*)(data + ( i + (int)( rn * (num - i ) ) )* step)), \
|
|
tmp ); \
|
|
} \
|
|
}
|
|
|
|
ICV_RAND_SHUFFLE( 8U, uchar )
|
|
|
|
ICV_RAND_SHUFFLE( 16S, short )
|
|
|
|
ICV_RAND_SHUFFLE( 32S, int )
|
|
|
|
ICV_RAND_SHUFFLE( 32F, float )
|
|
|
|
CV_BOOST_IMPL
|
|
void cvRandShuffleVec( CvMat* mat )
|
|
{
|
|
CV_FUNCNAME( "cvRandShuffle" );
|
|
|
|
__BEGIN__;
|
|
|
|
uchar* data;
|
|
size_t step;
|
|
int num;
|
|
|
|
if( (mat == NULL) || !CV_IS_MAT( mat ) || MIN( mat->rows, mat->cols ) != 1 )
|
|
{
|
|
CV_ERROR( CV_StsUnsupportedFormat, "" );
|
|
}
|
|
|
|
CV_MAT2VEC( *mat, data, step, num );
|
|
switch( CV_MAT_TYPE( mat->type ) )
|
|
{
|
|
case CV_8UC1:
|
|
icvRandShuffle_8U( data, step, num);
|
|
break;
|
|
case CV_16SC1:
|
|
icvRandShuffle_16S( data, step, num);
|
|
break;
|
|
case CV_32SC1:
|
|
icvRandShuffle_32S( data, step, num);
|
|
break;
|
|
case CV_32FC1:
|
|
icvRandShuffle_32F( data, step, num);
|
|
break;
|
|
default:
|
|
CV_ERROR( CV_StsUnsupportedFormat, "" );
|
|
}
|
|
|
|
__END__;
|
|
}
|
|
|
|
/* End of file. */
|