Commit ecf2c2945403e1359f13ebc7e6c6e20d2d63fa19

Authored by JordanCheney
2 parents 680dc867 d097bb1a

Merge pull request #363 from biometrics/cascade

OpenCV Cascade Integration
openbr/core/boost.cpp 0 → 100644
  1 +#include <opencv2/imgproc/imgproc.hpp>
  2 +
  3 +#include "boost.h"
  4 +#include "cxmisc.h"
  5 +
  6 +using namespace std;
  7 +using namespace br;
  8 +using namespace cv;
  9 +
  10 +static inline double
  11 +logRatio( double val )
  12 +{
  13 + const double eps = 1e-5;
  14 +
  15 + val = max( val, eps );
  16 + val = min( val, 1. - eps );
  17 + return log( val/(1. - val) );
  18 +}
  19 +
  20 +#define CV_CMP_NUM_IDX(i,j) (aux[i] < aux[j])
  21 +static CV_IMPLEMENT_QSORT_EX( icvSortIntAux, int, CV_CMP_NUM_IDX, const float* )
  22 +static CV_IMPLEMENT_QSORT_EX( icvSortUShAux, unsigned short, CV_CMP_NUM_IDX, const float* )
  23 +
  24 +#define CV_THRESHOLD_EPS (0.00001F)
  25 +
  26 +static const int MinBlockSize = 1 << 16;
  27 +static const int BlockSizeDelta = 1 << 10;
  28 +
  29 +// TODO remove this code duplication with ml/precomp.hpp
  30 +
  31 +static int CV_CDECL icvCmpIntegers( const void* a, const void* b )
  32 +{
  33 + return *(const int*)a - *(const int*)b;
  34 +}
  35 +
  36 +static CvMat* cvPreprocessIndexArray( const CvMat* idx_arr, int data_arr_size, bool check_for_duplicates=false, const int channels = 1 )
  37 +{
  38 + CvMat* idx = 0;
  39 +
  40 + CV_FUNCNAME( "cvPreprocessIndexArray" );
  41 +
  42 + __BEGIN__;
  43 +
  44 + int i, idx_total, idx_selected = 0, step, type, prev = INT_MIN, is_sorted = 1;
  45 + uchar* srcb = 0;
  46 + int* srci = 0;
  47 + int* dsti;
  48 +
  49 + if( !CV_IS_MAT(idx_arr) )
  50 + CV_ERROR( CV_StsBadArg, "Invalid index array" );
  51 +
  52 + if( idx_arr->rows != 1 && idx_arr->cols != 1 )
  53 + CV_ERROR( CV_StsBadSize, "the index array must be 1-dimensional" );
  54 +
  55 + idx_total = idx_arr->rows + idx_arr->cols - 1;
  56 + srcb = idx_arr->data.ptr;
  57 + srci = idx_arr->data.i;
  58 +
  59 + type = CV_MAT_TYPE(idx_arr->type);
  60 + step = CV_IS_MAT_CONT(idx_arr->type) ? 1 : idx_arr->step/CV_ELEM_SIZE(type);
  61 +
  62 + if (type == CV_8UC(channels) || type == CV_8SC1) {
  63 + // idx_arr is array of 1's and 0's -
  64 + // i.e. it is a mask of the selected components
  65 + if( idx_total != data_arr_size )
  66 + CV_ERROR( CV_StsUnmatchedSizes,
  67 + "Component mask should contain as many elements as the total number of input variables" );
  68 +
  69 + for( i = 0; i < idx_total; i++ )
  70 + idx_selected += srcb[i*step] != 0;
  71 +
  72 + if( idx_selected == 0 )
  73 + CV_ERROR( CV_StsOutOfRange, "No components/input_variables is selected!" );
  74 + } else if (type == CV_32SC(channels)) {
  75 + // idx_arr is array of integer indices of selected components
  76 + if( idx_total > data_arr_size )
  77 + CV_ERROR( CV_StsOutOfRange,
  78 + "index array may not contain more elements than the total number of input variables" );
  79 + idx_selected = idx_total;
  80 + // check if sorted already
  81 + for( i = 0; i < idx_total; i++ )
  82 + {
  83 + int val = srci[i*step];
  84 + if( val >= prev )
  85 + {
  86 + is_sorted = 0;
  87 + break;
  88 + }
  89 + prev = val;
  90 + }
  91 + } else {
  92 + CV_ERROR( CV_StsUnsupportedFormat, "Unsupported index array data type "
  93 + "(it should be 8uC1, 8sC1 or 32sC1)" );
  94 + }
  95 +
  96 + CV_CALL( idx = cvCreateMat( 1, idx_selected, CV_32SC(channels) ));
  97 + dsti = idx->data.i;
  98 +
  99 + if( type < CV_32SC(channels) )
  100 + {
  101 + for( i = 0; i < idx_total; i++ )
  102 + if( srcb[i*step] )
  103 + *dsti++ = i;
  104 + }
  105 + else
  106 + {
  107 + for( i = 0; i < idx_total; i++ )
  108 + dsti[i] = srci[i*step];
  109 +
  110 + if( !is_sorted )
  111 + qsort( dsti, idx_total, sizeof(dsti[0]), icvCmpIntegers );
  112 +
  113 + if( dsti[0] < 0 || dsti[idx_total-1] >= data_arr_size )
  114 + CV_ERROR( CV_StsOutOfRange, "the index array elements are out of range" );
  115 +
  116 + if( check_for_duplicates )
  117 + {
  118 + for( i = 1; i < idx_total; i++ )
  119 + if( dsti[i] <= dsti[i-1] )
  120 + CV_ERROR( CV_StsBadArg, "There are duplicated index array elements" );
  121 + }
  122 + }
  123 +
  124 + __END__;
  125 +
  126 + if( cvGetErrStatus() < 0 )
  127 + cvReleaseMat( &idx );
  128 +
  129 + return idx;
  130 +}
  131 +
  132 +//------------------------------------- FeatureEvaluator ---------------------------------------
  133 +
  134 +void FeatureEvaluator::init(Representation *_representation, int _maxSampleCount, int channels)
  135 +{
  136 + representation = _representation;
  137 +
  138 + int dx, dy;
  139 + Size windowSize = representation->windowSize(&dx, &dy);
  140 + data.create((int)_maxSampleCount, (windowSize.width + dx) * (windowSize.height + dy), CV_32SC(channels));
  141 + cls.create( (int)_maxSampleCount, 1, CV_32FC1 );
  142 +}
  143 +
  144 +void FeatureEvaluator::setImage(const Mat &img, uchar clsLabel, int idx)
  145 +{
  146 + cls.ptr<float>(idx)[0] = clsLabel;
  147 +
  148 + Mat pp;
  149 + representation->preprocess(img, pp);
  150 + pp.reshape(0, 1).copyTo(data.row(idx));
  151 +}
  152 +
  153 +//----------------------------- CascadeBoostParams -------------------------------------------------
  154 +
  155 +CascadeBoostParams::CascadeBoostParams() : minHitRate( 0.995F), maxFalseAlarm( 0.5F )
  156 +{
  157 + boost_type = CvBoost::GENTLE;
  158 + use_surrogates = use_1se_rule = truncate_pruned_tree = false;
  159 +}
  160 +
  161 +CascadeBoostParams::CascadeBoostParams(int _boostType,
  162 + float _minHitRate, float _maxFalseAlarm,
  163 + double _weightTrimRate, int _maxDepth, int _maxWeakCount ) :
  164 + CvBoostParams( _boostType, _maxWeakCount, _weightTrimRate, _maxDepth, false, 0 )
  165 +{
  166 + boost_type = _boostType;
  167 + minHitRate = _minHitRate;
  168 + maxFalseAlarm = _maxFalseAlarm;
  169 + use_surrogates = use_1se_rule = truncate_pruned_tree = false;
  170 +}
  171 +
  172 +//---------------------------- CascadeBoostTrainData -----------------------------
  173 +
  174 +struct CascadeBoostTrainData : CvDTreeTrainData
  175 +{
  176 + CascadeBoostTrainData(const FeatureEvaluator* _featureEvaluator, int _channels, const CvDTreeParams& _params);
  177 + CascadeBoostTrainData(const FeatureEvaluator* _featureEvaluator,
  178 + int _numSamples, int _precalcValBufSize, int _precalcIdxBufSize, int _channels,
  179 + const CvDTreeParams& _params = CvDTreeParams());
  180 + virtual void setData(const FeatureEvaluator* _featureEvaluator,
  181 + int _numSamples, int _precalcValBufSize, int _precalcIdxBufSize,
  182 + const CvDTreeParams& _params=CvDTreeParams());
  183 + void precalculate();
  184 +
  185 + virtual CvDTreeNode* subsample_data(const CvMat* _subsample_idx);
  186 +
  187 + virtual const int* get_class_labels(CvDTreeNode* n, int* labelsBuf);
  188 + virtual const int* get_cv_labels(CvDTreeNode* n, int* labelsBuf);
  189 + virtual const int* get_sample_indices(CvDTreeNode* n, int* indicesBuf);
  190 +
  191 + virtual void get_ord_var_data(CvDTreeNode* n, int vi, float* ordValuesBuf, int* sortedIndicesBuf,
  192 + const float** ordValues, const int** sortedIndices, int* sampleIndicesBuf);
  193 + virtual const int* get_cat_var_data(CvDTreeNode* n, int vi, int* catValuesBuf);
  194 + virtual float getVarValue(int vi, int si);
  195 + virtual void free_train_data();
  196 +
  197 + const FeatureEvaluator* featureEvaluator;
  198 + cv::Mat valCache; // precalculated feature values (CV_32FC1)
  199 + CvMat _resp; // for casting
  200 + int numPrecalcVal, numPrecalcIdx, channels;
  201 +};
  202 +
  203 +CvDTreeNode* CascadeBoostTrainData::subsample_data( const CvMat* _subsample_idx )
  204 +{
  205 + CvDTreeNode* root = 0;
  206 + CvMat* isubsample_idx = 0;
  207 + CvMat* subsample_co = 0;
  208 +
  209 + bool isMakeRootCopy = true;
  210 +
  211 + if( !data_root )
  212 + CV_Error( CV_StsError, "No training data has been set" );
  213 +
  214 + if( _subsample_idx )
  215 + {
  216 + CV_Assert( (isubsample_idx = cvPreprocessIndexArray( _subsample_idx, sample_count, channels )) != 0 );
  217 +
  218 + if( isubsample_idx->cols + isubsample_idx->rows - 1 == sample_count )
  219 + {
  220 + const int* sidx = isubsample_idx->data.i;
  221 + for( int i = 0; i < sample_count; i++ )
  222 + {
  223 + if( sidx[i] != i )
  224 + {
  225 + isMakeRootCopy = false;
  226 + break;
  227 + }
  228 + }
  229 + }
  230 + else
  231 + isMakeRootCopy = false;
  232 + }
  233 +
  234 + if( isMakeRootCopy )
  235 + {
  236 + // make a copy of the root node
  237 + CvDTreeNode temp;
  238 + int i;
  239 + root = new_node( 0, 1, 0, 0 );
  240 + temp = *root;
  241 + *root = *data_root;
  242 + root->num_valid = temp.num_valid;
  243 + if( root->num_valid )
  244 + {
  245 + for( i = 0; i < var_count; i++ )
  246 + root->num_valid[i] = data_root->num_valid[i];
  247 + }
  248 + root->cv_Tn = temp.cv_Tn;
  249 + root->cv_node_risk = temp.cv_node_risk;
  250 + root->cv_node_error = temp.cv_node_error;
  251 + }
  252 + else
  253 + {
  254 + int* sidx = isubsample_idx->data.i;
  255 + // co - array of count/offset pairs (to handle duplicated values in _subsample_idx)
  256 + int* co, cur_ofs = 0;
  257 + int workVarCount = get_work_var_count();
  258 + int count = isubsample_idx->rows + isubsample_idx->cols - 1;
  259 +
  260 + root = new_node( 0, count, 1, 0 );
  261 +
  262 + CV_Assert( (subsample_co = cvCreateMat( 1, sample_count*2, CV_32SC(channels) )) != 0);
  263 + cvZero( subsample_co );
  264 + co = subsample_co->data.i;
  265 + for( int i = 0; i < count; i++ )
  266 + co[sidx[i]*2]++;
  267 + for( int i = 0; i < sample_count; i++ )
  268 + {
  269 + if( co[i*2] )
  270 + {
  271 + co[i*2+1] = cur_ofs;
  272 + cur_ofs += co[i*2];
  273 + }
  274 + else
  275 + co[i*2+1] = -1;
  276 + }
  277 +
  278 + cv::AutoBuffer<uchar> inn_buf(sample_count*(2*sizeof(int) + sizeof(float)));
  279 + // subsample ordered variables
  280 + for( int vi = 0; vi < numPrecalcIdx; vi++ )
  281 + {
  282 + int ci = get_var_type(vi);
  283 + CV_Assert( ci < 0 );
  284 +
  285 + int *src_idx_buf = (int*)(uchar*)inn_buf;
  286 + float *src_val_buf = (float*)(src_idx_buf + sample_count);
  287 + int* sample_indices_buf = (int*)(src_val_buf + sample_count);
  288 + const int* src_idx = 0;
  289 + const float* src_val = 0;
  290 + get_ord_var_data( data_root, vi, src_val_buf, src_idx_buf, &src_val, &src_idx, sample_indices_buf );
  291 +
  292 + int j = 0, idx, count_i;
  293 + int num_valid = data_root->get_num_valid(vi);
  294 + CV_Assert( num_valid == sample_count );
  295 +
  296 + if (is_buf_16u)
  297 + {
  298 + unsigned short* udst_idx = (unsigned short*)(buf->data.s + root->buf_idx*get_length_subbuf() +
  299 + vi*sample_count + data_root->offset);
  300 + for( int i = 0; i < num_valid; i++ )
  301 + {
  302 + idx = src_idx[i];
  303 + count_i = co[idx*2];
  304 + if( count_i )
  305 + for( cur_ofs = co[idx*2+1]; count_i > 0; count_i--, j++, cur_ofs++ )
  306 + udst_idx[j] = (unsigned short)cur_ofs;
  307 + }
  308 + }
  309 + else
  310 + {
  311 + int* idst_idx = buf->data.i + root->buf_idx*get_length_subbuf() +
  312 + vi*sample_count + root->offset;
  313 + for( int i = 0; i < num_valid; i++ )
  314 + {
  315 + idx = src_idx[i];
  316 + count_i = co[idx*2];
  317 + if( count_i )
  318 + for( cur_ofs = co[idx*2+1]; count_i > 0; count_i--, j++, cur_ofs++ )
  319 + idst_idx[j] = cur_ofs;
  320 + }
  321 + }
  322 + }
  323 +
  324 + // subsample cv_lables
  325 + const int* src_lbls = get_cv_labels(data_root, (int*)(uchar*)inn_buf);
  326 + if (is_buf_16u)
  327 + {
  328 + unsigned short* udst = (unsigned short*)(buf->data.s + root->buf_idx*get_length_subbuf() +
  329 + (workVarCount-1)*sample_count + root->offset);
  330 + for( int i = 0; i < count; i++ )
  331 + udst[i] = (unsigned short)src_lbls[sidx[i]];
  332 + }
  333 + else
  334 + {
  335 + int* idst = buf->data.i + root->buf_idx*get_length_subbuf() +
  336 + (workVarCount-1)*sample_count + root->offset;
  337 + for( int i = 0; i < count; i++ )
  338 + idst[i] = src_lbls[sidx[i]];
  339 + }
  340 +
  341 + // subsample sample_indices
  342 + const int* sample_idx_src = get_sample_indices(data_root, (int*)(uchar*)inn_buf);
  343 + if (is_buf_16u)
  344 + {
  345 + unsigned short* sample_idx_dst = (unsigned short*)(buf->data.s + root->buf_idx*get_length_subbuf() +
  346 + workVarCount*sample_count + root->offset);
  347 + for( int i = 0; i < count; i++ )
  348 + sample_idx_dst[i] = (unsigned short)sample_idx_src[sidx[i]];
  349 + }
  350 + else
  351 + {
  352 + int* sample_idx_dst = buf->data.i + root->buf_idx*get_length_subbuf() +
  353 + workVarCount*sample_count + root->offset;
  354 + for( int i = 0; i < count; i++ )
  355 + sample_idx_dst[i] = sample_idx_src[sidx[i]];
  356 + }
  357 +
  358 + for( int vi = 0; vi < var_count; vi++ )
  359 + root->set_num_valid(vi, count);
  360 + }
  361 +
  362 + cvReleaseMat( &isubsample_idx );
  363 + cvReleaseMat( &subsample_co );
  364 +
  365 + return root;
  366 +}
  367 +
  368 +CascadeBoostTrainData::CascadeBoostTrainData(const FeatureEvaluator* _featureEvaluator,
  369 + int _channels,
  370 + const CvDTreeParams& _params)
  371 +{
  372 + is_classifier = true;
  373 + var_all = var_count = (int)_featureEvaluator->getNumFeatures();
  374 +
  375 + featureEvaluator = _featureEvaluator;
  376 + channels = _channels;
  377 + shared = true;
  378 + set_params( _params );
  379 + max_c_count = MAX( 2, featureEvaluator->getMaxCatCount() );
  380 + var_type = cvCreateMat( 1, var_count + 2, CV_32SC(channels) );
  381 + if ( featureEvaluator->getMaxCatCount() > 0 )
  382 + {
  383 + numPrecalcIdx = 0;
  384 + cat_var_count = var_count;
  385 + ord_var_count = 0;
  386 + for( int vi = 0; vi < var_count; vi++ )
  387 + {
  388 + var_type->data.i[vi] = vi;
  389 + }
  390 + }
  391 + else
  392 + {
  393 + cat_var_count = 0;
  394 + ord_var_count = var_count;
  395 + for( int vi = 1; vi <= var_count; vi++ )
  396 + {
  397 + var_type->data.i[vi-1] = -vi;
  398 + }
  399 + }
  400 + var_type->data.i[var_count] = cat_var_count;
  401 + var_type->data.i[var_count+1] = cat_var_count+1;
  402 +
  403 + int maxSplitSize = cvAlign(sizeof(CvDTreeSplit) + (MAX(0,max_c_count - 33)/32)*sizeof(int),sizeof(void*));
  404 + int treeBlockSize = MAX((int)sizeof(CvDTreeNode)*8, maxSplitSize);
  405 + treeBlockSize = MAX(treeBlockSize + BlockSizeDelta, MinBlockSize);
  406 + tree_storage = cvCreateMemStorage( treeBlockSize );
  407 + node_heap = cvCreateSet( 0, sizeof(node_heap[0]), sizeof(CvDTreeNode), tree_storage );
  408 + split_heap = cvCreateSet( 0, sizeof(split_heap[0]), maxSplitSize, tree_storage );
  409 +}
  410 +
  411 +CascadeBoostTrainData::CascadeBoostTrainData(const FeatureEvaluator* _featureEvaluator,
  412 + int _numSamples,
  413 + int _precalcValBufSize, int _precalcIdxBufSize, int _channels ,
  414 + const CvDTreeParams& _params)
  415 +{
  416 + channels = _channels;
  417 + setData( _featureEvaluator, _numSamples, _precalcValBufSize, _precalcIdxBufSize, _params );
  418 +}
  419 +
  420 +void CascadeBoostTrainData::setData( const FeatureEvaluator* _featureEvaluator,
  421 + int _numSamples,
  422 + int _precalcValBufSize, int _precalcIdxBufSize,
  423 + const CvDTreeParams& _params )
  424 +{
  425 + int* idst = 0;
  426 + unsigned short* udst = 0;
  427 +
  428 + uint64 effective_buf_size = 0;
  429 + int effective_buf_height = 0, effective_buf_width = 0;
  430 +
  431 + clear();
  432 + shared = true;
  433 + have_labels = true;
  434 + have_priors = false;
  435 + is_classifier = true;
  436 +
  437 + rng = &cv::theRNG();
  438 +
  439 + set_params( _params );
  440 +
  441 + CV_Assert( _featureEvaluator );
  442 + featureEvaluator = _featureEvaluator;
  443 +
  444 + max_c_count = MAX( 2, featureEvaluator->getMaxCatCount() );
  445 + _resp = featureEvaluator->getCls();
  446 + responses = &_resp;
  447 + // TODO: check responses: elements must be 0 or 1
  448 +
  449 + if( _precalcValBufSize < 0 || _precalcIdxBufSize < 0)
  450 + CV_Error( CV_StsOutOfRange, "_numPrecalcVal and _numPrecalcIdx must be positive or 0" );
  451 +
  452 + var_count = var_all = featureEvaluator->getNumFeatures() * featureEvaluator->getFeatureSize();
  453 + sample_count = _numSamples;
  454 +
  455 + is_buf_16u = false;
  456 + if (sample_count < 65536)
  457 + is_buf_16u = true;
  458 +
  459 + numPrecalcVal = min( cvRound((double)_precalcValBufSize*1048576. / (sizeof(float)*sample_count)), var_count );
  460 + numPrecalcIdx = min( cvRound((double)_precalcIdxBufSize*1048576. /
  461 + ((is_buf_16u ? sizeof(unsigned short) : sizeof (int))*sample_count)), var_count );
  462 +
  463 + assert( numPrecalcIdx >= 0 && numPrecalcVal >= 0 );
  464 +
  465 + valCache.create( numPrecalcVal, sample_count, CV_32FC1 );
  466 + var_type = cvCreateMat( 1, var_count + 2, CV_32SC(channels) );
  467 + if ( featureEvaluator->getMaxCatCount() > 0 )
  468 + {
  469 + numPrecalcIdx = 0;
  470 + cat_var_count = var_count;
  471 + ord_var_count = 0;
  472 + for( int vi = 0; vi < var_count; vi++ )
  473 + {
  474 + var_type->data.i[vi] = vi;
  475 + }
  476 + }
  477 + else
  478 + {
  479 + cat_var_count = 0;
  480 + ord_var_count = var_count;
  481 + for( int vi = 1; vi <= var_count; vi++ )
  482 + {
  483 + var_type->data.i[vi-1] = -vi;
  484 + }
  485 + }
  486 + var_type->data.i[var_count] = cat_var_count;
  487 + var_type->data.i[var_count+1] = cat_var_count+1;
  488 + work_var_count = ( cat_var_count ? 0 : numPrecalcIdx ) + 1/*cv_lables*/;
  489 + buf_count = 2;
  490 +
  491 + buf_size = -1; // the member buf_size is obsolete
  492 +
  493 + effective_buf_size = (uint64)(work_var_count + 1)*(uint64)sample_count * buf_count; // this is the total size of "CvMat buf" to be allocated
  494 + effective_buf_width = sample_count;
  495 + effective_buf_height = work_var_count+1;
  496 +
  497 + if (effective_buf_width >= effective_buf_height)
  498 + effective_buf_height *= buf_count;
  499 + else
  500 + effective_buf_width *= buf_count;
  501 +
  502 + if ((uint64)effective_buf_width * (uint64)effective_buf_height != effective_buf_size)
  503 + {
  504 + CV_Error(CV_StsBadArg, "The memory buffer cannot be allocated since its size exceeds integer fields limit");
  505 + }
  506 + if ( is_buf_16u )
  507 + buf = cvCreateMat( effective_buf_height, effective_buf_width, CV_16UC(channels) );
  508 + else
  509 + buf = cvCreateMat( effective_buf_height, effective_buf_width, CV_32SC(channels) );
  510 +
  511 + cat_count = cvCreateMat( 1, cat_var_count + 1, CV_32SC(channels) );
  512 +
  513 + // precalculate valCache and set indices in buf
  514 + precalculate();
  515 +
  516 + // now calculate the maximum size of split,
  517 + // create memory storage that will keep nodes and splits of the decision tree
  518 + // allocate root node and the buffer for the whole training data
  519 + int maxSplitSize = cvAlign(sizeof(CvDTreeSplit) +
  520 + (MAX(0,sample_count - 33)/32)*sizeof(int),sizeof(void*));
  521 + int treeBlockSize = MAX((int)sizeof(CvDTreeNode)*8, maxSplitSize);
  522 + treeBlockSize = MAX(treeBlockSize + BlockSizeDelta, MinBlockSize);
  523 + tree_storage = cvCreateMemStorage( treeBlockSize );
  524 + node_heap = cvCreateSet( 0, sizeof(*node_heap), sizeof(CvDTreeNode), tree_storage );
  525 +
  526 + int nvSize = var_count*sizeof(int);
  527 + nvSize = cvAlign(MAX( nvSize, (int)sizeof(CvSetElem) ), sizeof(void*));
  528 + int tempBlockSize = nvSize;
  529 + tempBlockSize = MAX( tempBlockSize + BlockSizeDelta, MinBlockSize );
  530 + temp_storage = cvCreateMemStorage( tempBlockSize );
  531 + nv_heap = cvCreateSet( 0, sizeof(*nv_heap), nvSize, temp_storage );
  532 +
  533 + data_root = new_node( 0, sample_count, 0, 0 );
  534 +
  535 + // set sample labels
  536 + if (is_buf_16u)
  537 + udst = (unsigned short*)(buf->data.s + work_var_count*sample_count);
  538 + else
  539 + idst = buf->data.i + work_var_count*sample_count;
  540 +
  541 + for (int si = 0; si < sample_count; si++)
  542 + {
  543 + if (udst)
  544 + udst[si] = (unsigned short)si;
  545 + else
  546 + idst[si] = si;
  547 + }
  548 + for( int vi = 0; vi < var_count; vi++ )
  549 + data_root->set_num_valid(vi, sample_count);
  550 + for( int vi = 0; vi < cat_var_count; vi++ )
  551 + cat_count->data.i[vi] = max_c_count;
  552 +
  553 + cat_count->data.i[cat_var_count] = 2;
  554 +
  555 + maxSplitSize = cvAlign(sizeof(CvDTreeSplit) +
  556 + (MAX(0,max_c_count - 33)/32)*sizeof(int),sizeof(void*));
  557 + split_heap = cvCreateSet( 0, sizeof(*split_heap), maxSplitSize, tree_storage );
  558 +
  559 + priors = cvCreateMat( 1, get_num_classes(), CV_64F );
  560 + cvSet(priors, cvScalar(1));
  561 + priors_mult = cvCloneMat( priors );
  562 + counts = cvCreateMat( 1, get_num_classes(), CV_32SC(channels) );
  563 + direction = cvCreateMat( 1, sample_count, CV_8UC(channels) );
  564 + split_buf = cvCreateMat( 1, sample_count, CV_32SC(channels) );//TODO: make a pointer
  565 +}
  566 +
  567 +void CascadeBoostTrainData::free_train_data()
  568 +{
  569 + CvDTreeTrainData::free_train_data();
  570 + valCache.release();
  571 +}
  572 +
  573 +const int* CascadeBoostTrainData::get_class_labels( CvDTreeNode* n, int* labelsBuf)
  574 +{
  575 + int nodeSampleCount = n->sample_count;
  576 + int rStep = CV_IS_MAT_CONT( responses->type ) ? 1 : responses->step / CV_ELEM_SIZE( responses->type );
  577 +
  578 + int* sampleIndicesBuf = labelsBuf; //
  579 + const int* sampleIndices = get_sample_indices(n, sampleIndicesBuf);
  580 + for( int si = 0; si < nodeSampleCount; si++ )
  581 + {
  582 + int sidx = sampleIndices[si];
  583 + labelsBuf[si] = (int)responses->data.fl[sidx*rStep];
  584 + }
  585 + return labelsBuf;
  586 +}
  587 +
  588 +const int* CascadeBoostTrainData::get_sample_indices( CvDTreeNode* n, int* indicesBuf )
  589 +{
  590 + return CvDTreeTrainData::get_cat_var_data( n, get_work_var_count(), indicesBuf );
  591 +}
  592 +
  593 +const int* CascadeBoostTrainData::get_cv_labels( CvDTreeNode* n, int* labels_buf )
  594 +{
  595 + return CvDTreeTrainData::get_cat_var_data( n, get_work_var_count() - 1, labels_buf );
  596 +}
  597 +
  598 +void CascadeBoostTrainData::get_ord_var_data( CvDTreeNode* n, int vi, float* ordValuesBuf, int* sortedIndicesBuf,
  599 + const float** ordValues, const int** sortedIndices, int* sampleIndicesBuf )
  600 +{
  601 + int nodeSampleCount = n->sample_count;
  602 + const int* sampleIndices = get_sample_indices(n, sampleIndicesBuf);
  603 +
  604 + if ( vi < numPrecalcIdx )
  605 + {
  606 + if( !is_buf_16u )
  607 + *sortedIndices = buf->data.i + n->buf_idx*get_length_subbuf() + vi*sample_count + n->offset;
  608 + else
  609 + {
  610 + const unsigned short* shortIndices = (const unsigned short*)(buf->data.s + n->buf_idx*get_length_subbuf() +
  611 + vi*sample_count + n->offset );
  612 + for( int i = 0; i < nodeSampleCount; i++ )
  613 + sortedIndicesBuf[i] = shortIndices[i];
  614 +
  615 + *sortedIndices = sortedIndicesBuf;
  616 + }
  617 +
  618 + if( vi < numPrecalcVal )
  619 + {
  620 + for( int i = 0; i < nodeSampleCount; i++ )
  621 + {
  622 + int idx = (*sortedIndices)[i];
  623 + idx = sampleIndices[idx];
  624 + ordValuesBuf[i] = valCache.at<float>( vi, idx);
  625 + }
  626 + }
  627 + else
  628 + {
  629 + for( int i = 0; i < nodeSampleCount; i++ )
  630 + {
  631 + int idx = (*sortedIndices)[i];
  632 + idx = sampleIndices[idx];
  633 + ordValuesBuf[i] = (*featureEvaluator)( vi, idx);
  634 + }
  635 + }
  636 + }
  637 + else // vi >= numPrecalcIdx
  638 + {
  639 + cv::AutoBuffer<float> abuf(nodeSampleCount);
  640 + float* sampleValues = &abuf[0];
  641 +
  642 + if ( vi < numPrecalcVal )
  643 + {
  644 + for( int i = 0; i < nodeSampleCount; i++ )
  645 + {
  646 + sortedIndicesBuf[i] = i;
  647 + sampleValues[i] = valCache.at<float>( vi, sampleIndices[i] );
  648 + }
  649 + }
  650 + else
  651 + {
  652 + for( int i = 0; i < nodeSampleCount; i++ )
  653 + {
  654 + sortedIndicesBuf[i] = i;
  655 + sampleValues[i] = (*featureEvaluator)( vi, sampleIndices[i]);
  656 + }
  657 + }
  658 + icvSortIntAux( sortedIndicesBuf, nodeSampleCount, &sampleValues[0] );
  659 + for( int i = 0; i < nodeSampleCount; i++ )
  660 + ordValuesBuf[i] = (&sampleValues[0])[sortedIndicesBuf[i]];
  661 + *sortedIndices = sortedIndicesBuf;
  662 + }
  663 +
  664 + *ordValues = ordValuesBuf;
  665 +}
  666 +
  667 +const int* CascadeBoostTrainData::get_cat_var_data( CvDTreeNode* n, int vi, int* catValuesBuf )
  668 +{
  669 + int nodeSampleCount = n->sample_count;
  670 + int* sampleIndicesBuf = catValuesBuf; //
  671 + const int* sampleIndices = get_sample_indices(n, sampleIndicesBuf);
  672 +
  673 + if ( vi < numPrecalcVal )
  674 + {
  675 + for( int i = 0; i < nodeSampleCount; i++ )
  676 + catValuesBuf[i] = (int) valCache.at<float>( vi, sampleIndices[i]);
  677 + }
  678 + else
  679 + {
  680 + if( vi >= numPrecalcVal && vi < var_count )
  681 + {
  682 + for( int i = 0; i < nodeSampleCount; i++ )
  683 + catValuesBuf[i] = (int)(*featureEvaluator)( vi, sampleIndices[i] );
  684 + }
  685 + else
  686 + {
  687 + get_cv_labels( n, catValuesBuf );
  688 + }
  689 + }
  690 +
  691 + return catValuesBuf;
  692 +}
  693 +
  694 +float CascadeBoostTrainData::getVarValue( int vi, int si )
  695 +{
  696 + if ( vi < numPrecalcVal && !valCache.empty() )
  697 + return valCache.at<float>( vi, si );
  698 + return (*featureEvaluator)( vi, si );
  699 +}
  700 +
  701 +struct FeatureIdxOnlyPrecalc : ParallelLoopBody
  702 +{
  703 + FeatureIdxOnlyPrecalc( const FeatureEvaluator* _featureEvaluator, CvMat* _buf, int _sample_count, bool _is_buf_16u )
  704 + {
  705 + featureEvaluator = _featureEvaluator;
  706 + sample_count = _sample_count;
  707 + udst = (unsigned short*)_buf->data.s;
  708 + idst = _buf->data.i;
  709 + is_buf_16u = _is_buf_16u;
  710 + }
  711 + void operator()( const Range& range ) const
  712 + {
  713 + cv::AutoBuffer<float> valCache(sample_count);
  714 + float* valCachePtr = (float*)valCache;
  715 + for ( int fi = range.start; fi < range.end; fi++)
  716 + {
  717 + for( int si = 0; si < sample_count; si++ )
  718 + {
  719 + valCachePtr[si] = (*featureEvaluator)( fi, si );
  720 + if ( is_buf_16u )
  721 + *(udst + fi*sample_count + si) = (unsigned short)si;
  722 + else
  723 + *(idst + fi*sample_count + si) = si;
  724 + }
  725 + if ( is_buf_16u )
  726 + icvSortUShAux( udst + fi*sample_count, sample_count, valCachePtr );
  727 + else
  728 + icvSortIntAux( idst + fi*sample_count, sample_count, valCachePtr );
  729 + }
  730 + }
  731 + const FeatureEvaluator* featureEvaluator;
  732 + int sample_count;
  733 + int* idst;
  734 + unsigned short* udst;
  735 + bool is_buf_16u;
  736 +};
  737 +
  738 +struct FeatureValAndIdxPrecalc : ParallelLoopBody
  739 +{
  740 + FeatureValAndIdxPrecalc( const FeatureEvaluator* _featureEvaluator, CvMat* _buf, Mat* _valCache, int _sample_count, bool _is_buf_16u )
  741 + {
  742 + featureEvaluator = _featureEvaluator;
  743 + valCache = _valCache;
  744 + sample_count = _sample_count;
  745 + udst = (unsigned short*)_buf->data.s;
  746 + idst = _buf->data.i;
  747 + is_buf_16u = _is_buf_16u;
  748 + }
  749 + void operator()( const Range& range ) const
  750 + {
  751 + for ( int fi = range.start; fi < range.end; fi++)
  752 + {
  753 + for( int si = 0; si < sample_count; si++ )
  754 + {
  755 + valCache->at<float>(fi,si) = (*featureEvaluator)( fi, si );
  756 + if ( is_buf_16u )
  757 + *(udst + fi*sample_count + si) = (unsigned short)si;
  758 + else
  759 + *(idst + fi*sample_count + si) = si;
  760 + }
  761 + if ( is_buf_16u )
  762 + icvSortUShAux( udst + fi*sample_count, sample_count, valCache->ptr<float>(fi) );
  763 + else
  764 + icvSortIntAux( idst + fi*sample_count, sample_count, valCache->ptr<float>(fi) );
  765 + }
  766 + }
  767 + const FeatureEvaluator* featureEvaluator;
  768 + Mat* valCache;
  769 + int sample_count;
  770 + int* idst;
  771 + unsigned short* udst;
  772 + bool is_buf_16u;
  773 +};
  774 +
  775 +struct FeatureValOnlyPrecalc : ParallelLoopBody
  776 +{
  777 + FeatureValOnlyPrecalc( const FeatureEvaluator* _featureEvaluator, Mat* _valCache, int _sample_count )
  778 + {
  779 + featureEvaluator = _featureEvaluator;
  780 + valCache = _valCache;
  781 + sample_count = _sample_count;
  782 + }
  783 + void operator()( const Range& range ) const
  784 + {
  785 + for ( int fi = range.start; fi < range.end; fi++)
  786 + for( int si = 0; si < sample_count; si++ )
  787 + valCache->at<float>(fi,si) = (*featureEvaluator)( fi, si );
  788 + }
  789 + const FeatureEvaluator* featureEvaluator;
  790 + Mat* valCache;
  791 + int sample_count;
  792 +};
  793 +
  794 +void CascadeBoostTrainData::precalculate()
  795 +{
  796 + int minNum = MIN( numPrecalcVal, numPrecalcIdx);
  797 +
  798 + double proctime = -TIME( 0 );
  799 + parallel_for_( Range(numPrecalcVal, numPrecalcIdx),
  800 + FeatureIdxOnlyPrecalc(featureEvaluator, buf, sample_count, is_buf_16u!=0) );
  801 + parallel_for_( Range(0, minNum),
  802 + FeatureValAndIdxPrecalc(featureEvaluator, buf, &valCache, sample_count, is_buf_16u!=0) );
  803 + parallel_for_( Range(minNum, numPrecalcVal),
  804 + FeatureValOnlyPrecalc(featureEvaluator, &valCache, sample_count) );
  805 + cout << "Precalculation time: " << (proctime + TIME( 0 )) << endl;
  806 +}
  807 +
  808 +//-------------------------------- CascadeBoostTree ----------------------------------------
  809 +
  810 +CvDTreeNode* CascadeBoostTree::predict( int sampleIdx ) const
  811 +{
  812 + CvDTreeNode* node = root;
  813 + if( !node )
  814 + CV_Error( CV_StsError, "The tree has not been trained yet" );
  815 +
  816 + if ( ((CascadeBoostTrainData*)data)->featureEvaluator->getMaxCatCount() == 0 ) // ordered
  817 + {
  818 + while( node->left )
  819 + {
  820 + CvDTreeSplit* split = node->split;
  821 + float val = ((CascadeBoostTrainData*)data)->getVarValue( split->var_idx, sampleIdx );
  822 + node = val <= split->ord.c ? node->left : node->right;
  823 + }
  824 + }
  825 + else // categorical
  826 + {
  827 + while( node->left )
  828 + {
  829 + CvDTreeSplit* split = node->split;
  830 + int c = (int)((CascadeBoostTrainData*)data)->getVarValue( split->var_idx, sampleIdx );
  831 + node = CV_DTREE_CAT_DIR(c, split->subset) < 0 ? node->left : node->right;
  832 + }
  833 + }
  834 + return node;
  835 +}
  836 +
  837 +void CascadeBoostTree::split_node_data( CvDTreeNode* node )
  838 +{
  839 + int n = node->sample_count, nl, nr, scount = data->sample_count;
  840 + char* dir = (char*)data->direction->data.ptr;
  841 + CvDTreeNode *left = 0, *right = 0;
  842 + int* newIdx = data->split_buf->data.i;
  843 + int newBufIdx = data->get_child_buf_idx( node );
  844 + int workVarCount = data->get_work_var_count();
  845 + CvMat* buf = data->buf;
  846 + size_t length_buf_row = data->get_length_subbuf();
  847 + cv::AutoBuffer<uchar> inn_buf(n*(3*sizeof(int)+sizeof(float)));
  848 + int* tempBuf = (int*)(uchar*)inn_buf;
  849 + bool splitInputData;
  850 +
  851 + complete_node_dir(node);
  852 +
  853 + for( int i = nl = nr = 0; i < n; i++ )
  854 + {
  855 + int d = dir[i];
  856 + // initialize new indices for splitting ordered variables
  857 + newIdx[i] = (nl & (d-1)) | (nr & -d); // d ? ri : li
  858 + nr += d;
  859 + nl += d^1;
  860 + }
  861 +
  862 + node->left = left = data->new_node( node, nl, newBufIdx, node->offset );
  863 + node->right = right = data->new_node( node, nr, newBufIdx, node->offset + nl );
  864 +
  865 + splitInputData = node->depth + 1 < data->params.max_depth &&
  866 + (node->left->sample_count > data->params.min_sample_count ||
  867 + node->right->sample_count > data->params.min_sample_count);
  868 +
  869 + // split ordered variables, keep both halves sorted.
  870 + for( int vi = 0; vi < ((CascadeBoostTrainData*)data)->numPrecalcIdx; vi++ )
  871 + {
  872 + int ci = data->get_var_type(vi);
  873 + if( ci >= 0 || !splitInputData )
  874 + continue;
  875 +
  876 + int n1 = node->get_num_valid(vi);
  877 + float *src_val_buf = (float*)(tempBuf + n);
  878 + int *src_sorted_idx_buf = (int*)(src_val_buf + n);
  879 + int *src_sample_idx_buf = src_sorted_idx_buf + n;
  880 + const int* src_sorted_idx = 0;
  881 + const float* src_val = 0;
  882 + data->get_ord_var_data(node, vi, src_val_buf, src_sorted_idx_buf, &src_val, &src_sorted_idx, src_sample_idx_buf);
  883 +
  884 + for(int i = 0; i < n; i++)
  885 + tempBuf[i] = src_sorted_idx[i];
  886 +
  887 + if (data->is_buf_16u)
  888 + {
  889 + ushort *ldst, *rdst;
  890 + ldst = (ushort*)(buf->data.s + left->buf_idx*length_buf_row +
  891 + vi*scount + left->offset);
  892 + rdst = (ushort*)(ldst + nl);
  893 +
  894 + // split sorted
  895 + for( int i = 0; i < n1; i++ )
  896 + {
  897 + int idx = tempBuf[i];
  898 + int d = dir[idx];
  899 + idx = newIdx[idx];
  900 + if (d)
  901 + {
  902 + *rdst = (ushort)idx;
  903 + rdst++;
  904 + }
  905 + else
  906 + {
  907 + *ldst = (ushort)idx;
  908 + ldst++;
  909 + }
  910 + }
  911 + CV_Assert( n1 == n );
  912 + }
  913 + else
  914 + {
  915 + int *ldst, *rdst;
  916 + ldst = buf->data.i + left->buf_idx*length_buf_row +
  917 + vi*scount + left->offset;
  918 + rdst = buf->data.i + right->buf_idx*length_buf_row +
  919 + vi*scount + right->offset;
  920 +
  921 + // split sorted
  922 + for( int i = 0; i < n1; i++ )
  923 + {
  924 + int idx = tempBuf[i];
  925 + int d = dir[idx];
  926 + idx = newIdx[idx];
  927 + if (d)
  928 + {
  929 + *rdst = idx;
  930 + rdst++;
  931 + }
  932 + else
  933 + {
  934 + *ldst = idx;
  935 + ldst++;
  936 + }
  937 + }
  938 + CV_Assert( n1 == n );
  939 + }
  940 + }
  941 +
  942 + // split cv_labels using newIdx relocation table
  943 + int *src_lbls_buf = tempBuf + n;
  944 + const int* src_lbls = data->get_cv_labels(node, src_lbls_buf);
  945 +
  946 + for(int i = 0; i < n; i++)
  947 + tempBuf[i] = src_lbls[i];
  948 +
  949 + if (data->is_buf_16u)
  950 + {
  951 + unsigned short *ldst = (unsigned short *)(buf->data.s + left->buf_idx*length_buf_row +
  952 + (workVarCount-1)*scount + left->offset);
  953 + unsigned short *rdst = (unsigned short *)(buf->data.s + right->buf_idx*length_buf_row +
  954 + (workVarCount-1)*scount + right->offset);
  955 +
  956 + for( int i = 0; i < n; i++ )
  957 + {
  958 + int idx = tempBuf[i];
  959 + if (dir[i])
  960 + {
  961 + *rdst = (unsigned short)idx;
  962 + rdst++;
  963 + }
  964 + else
  965 + {
  966 + *ldst = (unsigned short)idx;
  967 + ldst++;
  968 + }
  969 + }
  970 +
  971 + }
  972 + else
  973 + {
  974 + int *ldst = buf->data.i + left->buf_idx*length_buf_row +
  975 + (workVarCount-1)*scount + left->offset;
  976 + int *rdst = buf->data.i + right->buf_idx*length_buf_row +
  977 + (workVarCount-1)*scount + right->offset;
  978 +
  979 + for( int i = 0; i < n; i++ )
  980 + {
  981 + int idx = tempBuf[i];
  982 + if (dir[i])
  983 + {
  984 + *rdst = idx;
  985 + rdst++;
  986 + }
  987 + else
  988 + {
  989 + *ldst = idx;
  990 + ldst++;
  991 + }
  992 + }
  993 + }
  994 +
  995 + // split sample indices
  996 + int *sampleIdx_src_buf = tempBuf + n;
  997 + const int* sampleIdx_src = data->get_sample_indices(node, sampleIdx_src_buf);
  998 +
  999 + for(int i = 0; i < n; i++)
  1000 + tempBuf[i] = sampleIdx_src[i];
  1001 +
  1002 + if (data->is_buf_16u)
  1003 + {
  1004 + unsigned short* ldst = (unsigned short*)(buf->data.s + left->buf_idx*length_buf_row +
  1005 + workVarCount*scount + left->offset);
  1006 + unsigned short* rdst = (unsigned short*)(buf->data.s + right->buf_idx*length_buf_row +
  1007 + workVarCount*scount + right->offset);
  1008 + for (int i = 0; i < n; i++)
  1009 + {
  1010 + unsigned short idx = (unsigned short)tempBuf[i];
  1011 + if (dir[i])
  1012 + {
  1013 + *rdst = idx;
  1014 + rdst++;
  1015 + }
  1016 + else
  1017 + {
  1018 + *ldst = idx;
  1019 + ldst++;
  1020 + }
  1021 + }
  1022 + }
  1023 + else
  1024 + {
  1025 + int* ldst = buf->data.i + left->buf_idx*length_buf_row +
  1026 + workVarCount*scount + left->offset;
  1027 + int* rdst = buf->data.i + right->buf_idx*length_buf_row +
  1028 + workVarCount*scount + right->offset;
  1029 + for (int i = 0; i < n; i++)
  1030 + {
  1031 + int idx = tempBuf[i];
  1032 + if (dir[i])
  1033 + {
  1034 + *rdst = idx;
  1035 + rdst++;
  1036 + }
  1037 + else
  1038 + {
  1039 + *ldst = idx;
  1040 + ldst++;
  1041 + }
  1042 + }
  1043 + }
  1044 +
  1045 + for( int vi = 0; vi < data->var_count; vi++ )
  1046 + {
  1047 + left->set_num_valid(vi, (int)(nl));
  1048 + right->set_num_valid(vi, (int)(nr));
  1049 + }
  1050 +
  1051 + // deallocate the parent node data that is not needed anymore
  1052 + data->free_node_data(node);
  1053 +}
  1054 +
  1055 +//----------------------------------- CascadeBoost --------------------------------------
  1056 +
  1057 +void CascadeBoost::train(const FeatureEvaluator* _featureEvaluator,
  1058 + int _numSamples,
  1059 + int _precalcValBufSize, int _precalcIdxBufSize,
  1060 + int _channels,
  1061 + const CascadeBoostParams& _params)
  1062 +{
  1063 + CV_Assert(!data);
  1064 + clear();
  1065 +
  1066 + channels = _channels;
  1067 +
  1068 + data = new CascadeBoostTrainData(_featureEvaluator, _numSamples,
  1069 + _precalcValBufSize, _precalcIdxBufSize, channels, _params);
  1070 +
  1071 + set_params(_params);
  1072 + if ((_params.boost_type == LOGIT) || (_params.boost_type == GENTLE))
  1073 + data->do_responses_copy();
  1074 +
  1075 + update_weights(0);
  1076 +
  1077 + cout << "+----+---------+---------+" << endl;
  1078 + cout << "| N | HR | FA |" << endl;
  1079 + cout << "+----+---------+---------+" << endl;
  1080 +
  1081 + do
  1082 + {
  1083 + CascadeBoostTree* tree = new CascadeBoostTree;
  1084 + if (!tree->train( data, subsample_mask, this)) {
  1085 + delete tree;
  1086 + return;
  1087 + }
  1088 +
  1089 + classifiers.append(tree);
  1090 + update_weights(tree);
  1091 + trim_weights();
  1092 + if (cvCountNonZero(subsample_mask) == 0)
  1093 + return;
  1094 + }
  1095 + while (!isErrDesired() && (classifiers.size() < params.weak_count));
  1096 +
  1097 + clear();
  1098 +}
  1099 +
  1100 +float CascadeBoost::predict(int sampleIdx, bool returnSum) const
  1101 +{
  1102 + double sum = 0;
  1103 + foreach (const CvBoostTree *tree, classifiers)
  1104 + sum += ((CascadeBoostTree*)tree)->predict(sampleIdx)->value;
  1105 +
  1106 + if (!returnSum)
  1107 + sum = sum < threshold - CV_THRESHOLD_EPS ? 0.0 : 1.0;
  1108 + return (float)sum;
  1109 +}
  1110 +
  1111 +bool CascadeBoost::set_params(const CvBoostParams& _params)
  1112 +{
  1113 + minHitRate = ((CascadeBoostParams&)_params).minHitRate;
  1114 + maxFalseAlarm = ((CascadeBoostParams&)_params).maxFalseAlarm;
  1115 + return (( minHitRate > 0 ) && ( minHitRate < 1) &&
  1116 + (maxFalseAlarm > 0 ) && ( maxFalseAlarm < 1) &&
  1117 + CvBoost::set_params(_params));
  1118 +}
  1119 +
  1120 +void CascadeBoost::update_weights(CvBoostTree* tree)
  1121 +{
  1122 + int n = data->sample_count;
  1123 + double sumW = 0.;
  1124 + int step = 0;
  1125 + float* fdata = 0;
  1126 + int *sampleIdxBuf;
  1127 + const int* sampleIdx = 0;
  1128 + int inn_buf_size = ((params.boost_type == LOGIT) || (params.boost_type == GENTLE) ? n*sizeof(int) : 0) +
  1129 + ( !tree ? n*sizeof(int) : 0 );
  1130 + cv::AutoBuffer<uchar> inn_buf(inn_buf_size);
  1131 + uchar* cur_inn_buf_pos = (uchar*)inn_buf;
  1132 + if ( (params.boost_type == LOGIT) || (params.boost_type == GENTLE) )
  1133 + {
  1134 + step = CV_IS_MAT_CONT(data->responses_copy->type) ?
  1135 + 1 : data->responses_copy->step / CV_ELEM_SIZE(data->responses_copy->type);
  1136 + fdata = data->responses_copy->data.fl;
  1137 + sampleIdxBuf = (int*)cur_inn_buf_pos; cur_inn_buf_pos = (uchar*)(sampleIdxBuf + n);
  1138 + sampleIdx = data->get_sample_indices( data->data_root, sampleIdxBuf );
  1139 + }
  1140 + CvMat* buf = data->buf;
  1141 + size_t length_buf_row = data->get_length_subbuf();
  1142 + if( !tree ) // before training the first tree, initialize weights and other parameters
  1143 + {
  1144 + int* classLabelsBuf = (int*)cur_inn_buf_pos; cur_inn_buf_pos = (uchar*)(classLabelsBuf + n);
  1145 + const int* classLabels = data->get_class_labels(data->data_root, classLabelsBuf);
  1146 + // in case of logitboost and gentle adaboost each weak tree is a regression tree,
  1147 + // so we need to convert class labels to floating-point values
  1148 + double w0 = 1./n;
  1149 + double p[2] = { 1, 1 };
  1150 +
  1151 + cvReleaseMat( &orig_response );
  1152 + cvReleaseMat( &sum_response );
  1153 + cvReleaseMat( &weak_eval );
  1154 + cvReleaseMat( &subsample_mask );
  1155 + cvReleaseMat( &weights );
  1156 +
  1157 + orig_response = cvCreateMat( 1, n, CV_32S );
  1158 + weak_eval = cvCreateMat( 1, n, CV_64F );
  1159 + subsample_mask = cvCreateMat( 1, n, CV_8U );
  1160 + weights = cvCreateMat( 1, n, CV_64F );
  1161 + subtree_weights = cvCreateMat( 1, n + 2, CV_64F );
  1162 +
  1163 + if (data->is_buf_16u)
  1164 + {
  1165 + unsigned short* labels = (unsigned short*)(buf->data.s + data->data_root->buf_idx*length_buf_row +
  1166 + data->data_root->offset + (data->work_var_count-1)*data->sample_count);
  1167 + for( int i = 0; i < n; i++ )
  1168 + {
  1169 + // save original categorical responses {0,1}, convert them to {-1,1}
  1170 + orig_response->data.i[i] = classLabels[i]*2 - 1;
  1171 + // make all the samples active at start.
  1172 + // later, in trim_weights() deactivate/reactive again some, if need
  1173 + subsample_mask->data.ptr[i] = (uchar)1;
  1174 + // make all the initial weights the same.
  1175 + weights->data.db[i] = w0*p[classLabels[i]];
  1176 + // set the labels to find (from within weak tree learning proc)
  1177 + // the particular sample weight, and where to store the response.
  1178 + labels[i] = (unsigned short)i;
  1179 + }
  1180 + }
  1181 + else
  1182 + {
  1183 + int* labels = buf->data.i + data->data_root->buf_idx*length_buf_row +
  1184 + data->data_root->offset + (data->work_var_count-1)*data->sample_count;
  1185 +
  1186 + for( int i = 0; i < n; i++ )
  1187 + {
  1188 + // save original categorical responses {0,1}, convert them to {-1,1}
  1189 + orig_response->data.i[i] = classLabels[i]*2 - 1;
  1190 + subsample_mask->data.ptr[i] = (uchar)1;
  1191 + weights->data.db[i] = w0*p[classLabels[i]];
  1192 + labels[i] = i;
  1193 + }
  1194 + }
  1195 +
  1196 + if( params.boost_type == LOGIT )
  1197 + {
  1198 + sum_response = cvCreateMat( 1, n, CV_64F );
  1199 +
  1200 + for( int i = 0; i < n; i++ )
  1201 + {
  1202 + sum_response->data.db[i] = 0;
  1203 + fdata[sampleIdx[i]*step] = orig_response->data.i[i] > 0 ? 2.f : -2.f;
  1204 + }
  1205 +
  1206 + // in case of logitboost each weak tree is a regression tree.
  1207 + // the target function values are recalculated for each of the trees
  1208 + data->is_classifier = false;
  1209 + }
  1210 + else if( params.boost_type == GENTLE )
  1211 + {
  1212 + for( int i = 0; i < n; i++ )
  1213 + fdata[sampleIdx[i]*step] = (float)orig_response->data.i[i];
  1214 +
  1215 + data->is_classifier = false;
  1216 + }
  1217 + }
  1218 + else
  1219 + {
  1220 + // at this moment, for all the samples that participated in the training of the most
  1221 + // recent weak classifier we know the responses. For other samples we need to compute them
  1222 + if( have_subsample )
  1223 + {
  1224 + // invert the subsample mask
  1225 + cvXorS( subsample_mask, cvScalar(1.), subsample_mask );
  1226 +
  1227 + // run tree through all the non-processed samples
  1228 + for( int i = 0; i < n; i++ )
  1229 + if( subsample_mask->data.ptr[i] )
  1230 + {
  1231 + weak_eval->data.db[i] = ((CascadeBoostTree*)tree)->predict( i )->value;
  1232 + }
  1233 + }
  1234 +
  1235 + // now update weights and other parameters for each type of boosting
  1236 + if( params.boost_type == DISCRETE )
  1237 + {
  1238 + // Discrete AdaBoost:
  1239 + // weak_eval[i] (=f(x_i)) is in {-1,1}
  1240 + // err = sum(w_i*(f(x_i) != y_i))/sum(w_i)
  1241 + // C = log((1-err)/err)
  1242 + // w_i *= exp(C*(f(x_i) != y_i))
  1243 +
  1244 + double C, err = 0.;
  1245 + double scale[] = { 1., 0. };
  1246 +
  1247 + for( int i = 0; i < n; i++ )
  1248 + {
  1249 + double w = weights->data.db[i];
  1250 + sumW += w;
  1251 + err += w*(weak_eval->data.db[i] != orig_response->data.i[i]);
  1252 + }
  1253 +
  1254 + if( sumW != 0 )
  1255 + err /= sumW;
  1256 + C = err = -logRatio( err );
  1257 + scale[1] = exp(err);
  1258 +
  1259 + sumW = 0;
  1260 + for( int i = 0; i < n; i++ )
  1261 + {
  1262 + double w = weights->data.db[i]*
  1263 + scale[weak_eval->data.db[i] != orig_response->data.i[i]];
  1264 + sumW += w;
  1265 + weights->data.db[i] = w;
  1266 + }
  1267 +
  1268 + tree->scale( C );
  1269 + }
  1270 + else if( params.boost_type == REAL )
  1271 + {
  1272 + // Real AdaBoost:
  1273 + // weak_eval[i] = f(x_i) = 0.5*log(p(x_i)/(1-p(x_i))), p(x_i)=P(y=1|x_i)
  1274 + // w_i *= exp(-y_i*f(x_i))
  1275 +
  1276 + for( int i = 0; i < n; i++ )
  1277 + weak_eval->data.db[i] *= -orig_response->data.i[i];
  1278 +
  1279 + cvExp( weak_eval, weak_eval );
  1280 +
  1281 + for( int i = 0; i < n; i++ )
  1282 + {
  1283 + double w = weights->data.db[i]*weak_eval->data.db[i];
  1284 + sumW += w;
  1285 + weights->data.db[i] = w;
  1286 + }
  1287 + }
  1288 + else if( params.boost_type == LOGIT )
  1289 + {
  1290 + // LogitBoost:
  1291 + // weak_eval[i] = f(x_i) in [-z_max,z_max]
  1292 + // sum_response = F(x_i).
  1293 + // F(x_i) += 0.5*f(x_i)
  1294 + // p(x_i) = exp(F(x_i))/(exp(F(x_i)) + exp(-F(x_i))=1/(1+exp(-2*F(x_i)))
  1295 + // reuse weak_eval: weak_eval[i] <- p(x_i)
  1296 + // w_i = p(x_i)*1(1 - p(x_i))
  1297 + // z_i = ((y_i+1)/2 - p(x_i))/(p(x_i)*(1 - p(x_i)))
  1298 + // store z_i to the data->data_root as the new target responses
  1299 +
  1300 + const double lbWeightThresh = FLT_EPSILON;
  1301 + const double lbZMax = 10.;
  1302 +
  1303 + for( int i = 0; i < n; i++ )
  1304 + {
  1305 + double s = sum_response->data.db[i] + 0.5*weak_eval->data.db[i];
  1306 + sum_response->data.db[i] = s;
  1307 + weak_eval->data.db[i] = -2*s;
  1308 + }
  1309 +
  1310 + cvExp( weak_eval, weak_eval );
  1311 +
  1312 + for( int i = 0; i < n; i++ )
  1313 + {
  1314 + double p = 1./(1. + weak_eval->data.db[i]);
  1315 + double w = p*(1 - p), z;
  1316 + w = MAX( w, lbWeightThresh );
  1317 + weights->data.db[i] = w;
  1318 + sumW += w;
  1319 + if( orig_response->data.i[i] > 0 )
  1320 + {
  1321 + z = 1./p;
  1322 + fdata[sampleIdx[i]*step] = (float)min(z, lbZMax);
  1323 + }
  1324 + else
  1325 + {
  1326 + z = 1./(1-p);
  1327 + fdata[sampleIdx[i]*step] = (float)-min(z, lbZMax);
  1328 + }
  1329 + }
  1330 + }
  1331 + else
  1332 + {
  1333 + // Gentle AdaBoost:
  1334 + // weak_eval[i] = f(x_i) in [-1,1]
  1335 + // w_i *= exp(-y_i*f(x_i))
  1336 + assert( params.boost_type == GENTLE );
  1337 +
  1338 + for( int i = 0; i < n; i++ )
  1339 + weak_eval->data.db[i] *= -orig_response->data.i[i];
  1340 +
  1341 + cvExp( weak_eval, weak_eval );
  1342 +
  1343 + for( int i = 0; i < n; i++ )
  1344 + {
  1345 + double w = weights->data.db[i] * weak_eval->data.db[i];
  1346 + weights->data.db[i] = w;
  1347 + sumW += w;
  1348 + }
  1349 + }
  1350 + }
  1351 +
  1352 + // renormalize weights
  1353 + if( sumW > FLT_EPSILON )
  1354 + {
  1355 + sumW = 1./sumW;
  1356 + for( int i = 0; i < n; ++i )
  1357 + weights->data.db[i] *= sumW;
  1358 + }
  1359 +}
  1360 +
  1361 +bool CascadeBoost::isErrDesired()
  1362 +{
  1363 + QList<float> posVals;
  1364 + for (int i = 0; i < data->sample_count; i++)
  1365 + if (((CascadeBoostTrainData*)data)->featureEvaluator->getCls(i) == 1.0F)
  1366 + posVals.append(predict(i, true));
  1367 +
  1368 + std::sort(posVals.begin(), posVals.end());
  1369 +
  1370 + int thresholdIdx = (int)((1.0F - minHitRate) * posVals.size());
  1371 + threshold = posVals[thresholdIdx];
  1372 +
  1373 + int numPosTrue = posVals.size() - thresholdIdx;
  1374 + for (int i = thresholdIdx - 1; i >= 0; i--)
  1375 + if (abs(posVals[i] - threshold) < FLT_EPSILON)
  1376 + numPosTrue++;
  1377 + float hitRate = ((float)numPosTrue) / ((float)posVals.size());
  1378 +
  1379 + int numNeg = 0, numFalse = 0;
  1380 + for (int i = 0; i < data->sample_count; i++) {
  1381 + if (((CascadeBoostTrainData*)data)->featureEvaluator->getCls(i) == 0.0F) {
  1382 + numNeg++;
  1383 + if (predict(i))
  1384 + numFalse++;
  1385 + }
  1386 + }
  1387 + float falseAlarm = ((float)numFalse) / ((float)numNeg);
  1388 +
  1389 + cout << "|"; cout.width(4); cout << right << classifiers.size();
  1390 + cout << "|"; cout.width(9); cout << right << hitRate;
  1391 + cout << "|"; cout.width(9); cout << right << falseAlarm;
  1392 + cout << "|" << endl;
  1393 + cout << "+----+---------+---------+" << endl;
  1394 +
  1395 + return falseAlarm <= maxFalseAlarm;
  1396 +}
... ...
openbr/core/boost.h 0 → 100644
  1 +#ifndef _BOOST_H_
  2 +#define _BOOST_H_
  3 +
  4 +#include "ml.h"
  5 +#include <openbr/openbr_plugin.h>
  6 +
  7 +#ifdef _WIN32
  8 +#define TIME( arg ) (((double) clock()) / CLOCKS_PER_SEC)
  9 +#else
  10 +#define TIME( arg ) (time( arg ))
  11 +#endif
  12 +
  13 +namespace br
  14 +{
  15 +
  16 +struct FeatureEvaluator
  17 +{
  18 + ~FeatureEvaluator() {}
  19 + void init(Representation *_representation, int _maxSampleCount, int channels);
  20 + void setImage(const cv::Mat& img, uchar clsLabel, int idx);
  21 + float operator()(int featureIdx, int sampleIdx) const { return representation->evaluate(data.row(sampleIdx), featureIdx); }
  22 +
  23 + int getNumFeatures() const { return representation->numFeatures(); }
  24 + int getMaxCatCount() const { return representation->maxCatCount(); }
  25 + int getFeatureSize() const { return 1; }
  26 + const cv::Mat& getCls() const { return cls; }
  27 + float getCls(int si) const { return cls.at<float>(si, 0); }
  28 +
  29 + cv::Mat data, cls;
  30 + Representation *representation;
  31 +};
  32 +
  33 +struct CascadeBoostParams : CvBoostParams
  34 +{
  35 + float minHitRate;
  36 + float maxFalseAlarm;
  37 +
  38 + CascadeBoostParams();
  39 + CascadeBoostParams(int _boostType, float _minHitRate, float _maxFalseAlarm,
  40 + double _weightTrimRate, int _maxDepth, int _maxWeakCount);
  41 + virtual ~CascadeBoostParams() {}
  42 +};
  43 +
  44 +class CascadeBoostTree : public CvBoostTree
  45 +{
  46 +public:
  47 + using CvBoostTree::predict;
  48 + virtual CvDTreeNode* predict(int sampleIdx) const;
  49 +
  50 +protected:
  51 + virtual void split_node_data(CvDTreeNode* n);
  52 +};
  53 +
  54 +class CascadeBoost : public CvBoost
  55 +{
  56 +public:
  57 + using CvBoost::train;
  58 + virtual void train(const FeatureEvaluator *_featureEvaluator,
  59 + int _numSamples, int _precalcValBufSize, int _precalcIdxBufSize, int _channels,
  60 + const CascadeBoostParams &_params=CascadeBoostParams());
  61 +
  62 + using CvBoost::predict;
  63 + virtual float predict( int sampleIdx, bool returnSum = false ) const;
  64 +
  65 + float getThreshold() const { return threshold; }
  66 + QList<CvBoostTree *> getClassifers() const { return classifiers; }
  67 +
  68 +protected:
  69 + virtual bool set_params(const CvBoostParams& _params);
  70 + virtual void update_weights(CvBoostTree* tree);
  71 + virtual bool isErrDesired();
  72 +
  73 + QList<CvBoostTree *> classifiers;
  74 +
  75 + float threshold;
  76 + float minHitRate, maxFalseAlarm;
  77 + int channels;
  78 +};
  79 +
  80 +} // namespace br
  81 +
  82 +#endif
  83 +
... ...
openbr/core/opencvutils.cpp
... ... @@ -382,6 +382,129 @@ bool OpenCVUtils::overlaps(const QList&lt;Rect&gt; &amp;posRects, const Rect &amp;negRect, dou
382 382 return false;
383 383 }
384 384  
  385 +// class for grouping object candidates, detected by Cascade Classifier, HOG etc.
  386 +// instance of the class is to be passed to cv::partition (see cxoperations.hpp)
  387 +class SimilarRects
  388 +{
  389 +public:
  390 + SimilarRects(double _eps) : eps(_eps) {}
  391 + inline bool operator()(const Rect& r1, const Rect& r2) const
  392 + {
  393 + double delta = eps*(std::min(r1.width, r2.width) + std::min(r1.height, r2.height))*0.5;
  394 + return std::abs(r1.x - r2.x) <= delta &&
  395 + std::abs(r1.y - r2.y) <= delta &&
  396 + std::abs(r1.x + r1.width - r2.x - r2.width) <= delta &&
  397 + std::abs(r1.y + r1.height - r2.y - r2.height) <= delta;
  398 + }
  399 + double eps;
  400 +};
  401 +
  402 +// TODO: Make sure case where no confidences are inputted works.
  403 +void OpenCVUtils::group(vector<Rect> &rects, vector<float> &confidences, float confidenceThreshold, float epsilon)
  404 +{
  405 + if (rects.empty())
  406 + return;
  407 +
  408 + const bool useConfidences = !confidences.empty();
  409 +
  410 + vector<int> labels;
  411 + int nClasses = cv::partition(rects, labels, SimilarRects(epsilon));
  412 +
  413 + // Rect for each class (class meaning identity assigned by partition)
  414 + vector<Rect> rrects(nClasses);
  415 +
  416 + // Total number of rects in each class
  417 + vector<int> rweights(nClasses, 0);
  418 + vector<float> rejectWeights(nClasses, -std::numeric_limits<float>::max());
  419 +
  420 + for (int i = 0; i < labels.size(); i++)
  421 + {
  422 + int cls = labels[i];
  423 + rrects[cls].x += rects[i].x;
  424 + rrects[cls].y += rects[i].y;
  425 + rrects[cls].width += rects[i].width;
  426 + rrects[cls].height += rects[i].height;
  427 + rweights[cls]++;
  428 + }
  429 +
  430 + if (useConfidences)
  431 + {
  432 + // For each class, find maximum confidence
  433 + for (int i = 0; i < labels.size(); i++)
  434 + {
  435 + int cls = labels[i];
  436 + if (confidences[i] > rejectWeights[cls])
  437 + rejectWeights[cls] = confidences[i];
  438 + }
  439 + }
  440 +
  441 + // Find average rectangle for all classes
  442 + for (int i = 0; i < nClasses; i++)
  443 + {
  444 + Rect r = rrects[i];
  445 + float s = 1.f/rweights[i];
  446 + rrects[i] = Rect(saturate_cast<int>(r.x*s),
  447 + saturate_cast<int>(r.y*s),
  448 + saturate_cast<int>(r.width*s),
  449 + saturate_cast<int>(r.height*s));
  450 + }
  451 +
  452 + rects.clear();
  453 + confidences.clear();
  454 +
  455 + // Aggregate by comparing average rectangles against other average rectangels
  456 + for (int i = 0; i < nClasses; i++)
  457 + {
  458 + // Average rectangle
  459 + Rect r1 = rrects[i];
  460 +
  461 + // Used to eliminate rectangles with few neighbors in the case of no weights
  462 + // int n1 = levelWeights ? rejectLevels[i] : rweights[i];
  463 + float w1 = rejectWeights[i];
  464 +
  465 + // Eliminate rectangle if it doesn't meet confidence criteria
  466 + if (w1 <= confidenceThreshold)
  467 + continue;
  468 +
  469 + // filter out small face rectangles inside large rectangles
  470 + int j;
  471 + for (j = 0; j < nClasses; j++)
  472 + {
  473 + float w2 = rejectWeights[j];
  474 +
  475 + if (j == i)
  476 + continue;
  477 +
  478 + Rect r2 = rrects[j];
  479 +
  480 + int dx = saturate_cast<int>(r2.width * epsilon);
  481 + int dy = saturate_cast<int>(r2.height * epsilon);
  482 +
  483 + // If, r1 is within the r2 AND
  484 + // the second rectangle reaches a later stage than the first
  485 + // where both the first and the second must have a stage greater than three OR
  486 + // the first doens't reach the third stage.
  487 + // Changeto: second rectangle has a higher confidence than the first OR
  488 + // the first has a low confidence.
  489 + // Then, eliminate the first rectangle.
  490 + if(r1.x >= r2.x - dx &&
  491 + r1.y >= r2.y - dy &&
  492 + r1.x + r1.width <= r2.x + r2.width + dx &&
  493 + r1.y + r1.height <= r2.y + r2.height + dy &&
  494 + (w2 > std::max(confidenceThreshold, w1)))
  495 + break;
  496 + }
  497 +
  498 + // Need to return rects and confidences
  499 + if( j == nClasses )
  500 + {
  501 + rects.push_back(r1);
  502 + if (useConfidences)
  503 + confidences.push_back(w1);
  504 + }
  505 + }
  506 +}
  507 +
385 508 QDataStream &operator<<(QDataStream &stream, const Mat &m)
386 509 {
387 510 // Write header
... ...
openbr/core/opencvutils.h
... ... @@ -98,6 +98,9 @@ namespace OpenCVUtils
98 98 float overlap(const cv::Rect &rect1, const cv::Rect &rect2);
99 99 float overlap(const QRectF &rect1, const QRectF &rect2);
100 100  
  101 + // Misc
  102 + void group(std::vector<cv::Rect> &rects, std::vector<float> &confidences, float confidenceThreshold, float epsilon);
  103 +
101 104 int getFourcc();
102 105 }
103 106  
... ...
openbr/openbr_plugin.cpp
... ... @@ -1475,7 +1475,7 @@ Transform *Transform::make(QString str, QObject *parent)
1475 1475 Transform *Transform::clone() const
1476 1476 {
1477 1477 Transform *clone = Factory<Transform>::make("."+description(false));
1478   - return clone;
  1478 + return clone;
1479 1479 }
1480 1480  
1481 1481 static void _project(const Transform *transform, const Template *src, Template *dst)
... ...
openbr/openbr_plugin.h
... ... @@ -1401,12 +1401,18 @@ public:
1401 1401 virtual ~Representation() {}
1402 1402  
1403 1403 static Representation *make(QString str, QObject *parent); /*!< \brief Make a representation from a string. */
1404   - virtual cv::Mat preprocess(const cv::Mat &image) const { return image; }
  1404 + virtual void preprocess(const cv::Mat &src, cv::Mat &dst) const { dst = src; }
1405 1405 virtual void train(const QList<cv::Mat> &images, const QList<float> &labels) { (void) images; (void)labels; }
  1406 +
  1407 + virtual float evaluate(const cv::Mat &image, int idx) const = 0;
1406 1408 // By convention, an empty indices list will result in all feature responses being calculated
1407 1409 // and returned.
1408 1410 virtual cv::Mat evaluate(const cv::Mat &image, const QList<int> &indices = QList<int>()) const = 0;
  1411 +
  1412 + virtual cv::Size windowSize(int *dx = NULL, int *dy = NULL) const = 0; // dx and dy should indicate the change to the original window size after preprocessing
  1413 + virtual int numChannels() const { return 1; }
1409 1414 virtual int numFeatures() const = 0;
  1415 + virtual int maxCatCount() const = 0;
1410 1416 };
1411 1417  
1412 1418 class BR_EXPORT Classifier : public Object
... ... @@ -1417,10 +1423,15 @@ public:
1417 1423 virtual ~Classifier() {}
1418 1424  
1419 1425 static Classifier *make(QString str, QObject *parent); /*!< \brief Make a classifier from a string. */
  1426 +
1420 1427 virtual void train(const QList<cv::Mat> &images, const QList<float> &labels) = 0;
1421   - // By convention, classify should return a value normalized such that the threshold is 0. Negative values
1422   - // can be interpreted as a negative classification and positive values as a positive classification.
1423   - virtual float classify(const cv::Mat &image) const = 0;
  1428 + virtual float classify(const cv::Mat &image, bool process = true, float *confidence = NULL) const = 0;
  1429 +
  1430 + // Slots for representations
  1431 + virtual cv::Mat preprocess(const cv::Mat &image) const = 0;
  1432 + virtual cv::Size windowSize(int *dx = NULL, int *dy = NULL) const = 0;
  1433 + virtual int numFeatures() const = 0;
  1434 + virtual int maxCatCount() const = 0;
1424 1435 };
1425 1436  
1426 1437 /*!
... ...
openbr/plugins/classification/boostedforest.cpp 0 → 100644
  1 +#include <openbr/plugins/openbr_internal.h>
  2 +#include <openbr/core/boost.h>
  3 +
  4 +#define THRESHOLD_EPS 1e-5
  5 +
  6 +using namespace cv;
  7 +
  8 +namespace br
  9 +{
  10 +
  11 +struct Node
  12 +{
  13 + float value; // for leaf nodes
  14 +
  15 + float threshold; // for ordered features
  16 + QList<int> subset; // for categorical features
  17 + int featureIdx;
  18 +
  19 + Node *left, *right;
  20 +};
  21 +
  22 +static void buildTreeRecursive(Node *node, const CvDTreeNode *cv_node, int maxCatCount)
  23 +{
  24 + if (!cv_node->left) {
  25 + node->value = cv_node->value;
  26 + node->left = node->right = NULL;
  27 + } else {
  28 + if (maxCatCount > 0)
  29 + for (int i = 0; i < (maxCatCount + 31)/32; i++)
  30 + node->subset.append(cv_node->split->subset[i]);
  31 + else
  32 + node->threshold = cv_node->split->ord.c;
  33 +
  34 + node->featureIdx = cv_node->split->var_idx;
  35 +
  36 + node->left = new Node; node->right = new Node;
  37 + buildTreeRecursive(node->left, cv_node->left, maxCatCount);
  38 + buildTreeRecursive(node->right, cv_node->right, maxCatCount);
  39 + }
  40 +}
  41 +
  42 +static void loadRecursive(QDataStream &stream, Node *node, int maxCatCount)
  43 +{
  44 + bool hasChildren; stream >> hasChildren;
  45 +
  46 + if (!hasChildren) {
  47 + stream >> node->value;
  48 + node->left = node->right = NULL;
  49 + } else {
  50 + if (maxCatCount > 0)
  51 + for (int i = 0; i < (maxCatCount + 31)/32; i++) {
  52 + int s; stream >> s; node->subset.append(s);
  53 + }
  54 + else
  55 + stream >> node->threshold;
  56 +
  57 + stream >> node->featureIdx;
  58 +
  59 + node->left = new Node; node->right = new Node;
  60 + loadRecursive(stream, node->left, maxCatCount);
  61 + loadRecursive(stream, node->right, maxCatCount);
  62 + }
  63 +}
  64 +
  65 +static void storeRecursive(QDataStream &stream, const Node *node, int maxCatCount)
  66 +{
  67 + bool hasChildren = node->left ? true : false;
  68 + stream << hasChildren;
  69 +
  70 + if (!hasChildren)
  71 + stream << node->value;
  72 + else {
  73 + if (maxCatCount > 0)
  74 + for (int i = 0; i < (maxCatCount + 31)/32; i++)
  75 + stream << node->subset[i];
  76 + else
  77 + stream << node->threshold;
  78 +
  79 + stream << node->featureIdx;
  80 +
  81 + storeRecursive(stream, node->left, maxCatCount);
  82 + storeRecursive(stream, node->right, maxCatCount);
  83 + }
  84 +}
  85 +
  86 +class BoostedForestClassifier : public Classifier
  87 +{
  88 + Q_OBJECT
  89 + Q_ENUMS(Type)
  90 +
  91 + Q_PROPERTY(br::Representation* representation READ get_representation WRITE set_representation RESET reset_representation STORED false)
  92 + Q_PROPERTY(float minTAR READ get_minTAR WRITE set_minTAR RESET reset_minTAR STORED false)
  93 + Q_PROPERTY(float maxFAR READ get_maxFAR WRITE set_maxFAR RESET reset_maxFAR STORED false)
  94 + Q_PROPERTY(float trimRate READ get_trimRate WRITE set_trimRate RESET reset_trimRate STORED false)
  95 + Q_PROPERTY(int maxDepth READ get_maxDepth WRITE set_maxDepth RESET reset_maxDepth STORED false)
  96 + Q_PROPERTY(int maxWeakCount READ get_maxWeakCount WRITE set_maxWeakCount RESET reset_maxWeakCount STORED false)
  97 + Q_PROPERTY(Type type READ get_type WRITE set_type RESET reset_type STORED false)
  98 +
  99 +public:
  100 + enum Type { Discrete = CvBoost::DISCRETE,
  101 + Real = CvBoost::REAL,
  102 + Logit = CvBoost::LOGIT,
  103 + Gentle = CvBoost::GENTLE};
  104 +private:
  105 + BR_PROPERTY(br::Representation*, representation, NULL)
  106 + BR_PROPERTY(float, minTAR, 0.995)
  107 + BR_PROPERTY(float, maxFAR, 0.5)
  108 + BR_PROPERTY(float, trimRate, 0.95)
  109 + BR_PROPERTY(int, maxDepth, 1)
  110 + BR_PROPERTY(int, maxWeakCount, 100)
  111 + BR_PROPERTY(Type, type, Gentle)
  112 +
  113 + QList<Node*> classifiers;
  114 + float threshold;
  115 +
  116 + void train(const QList<Mat> &images, const QList<float> &labels)
  117 + {
  118 + representation->train(images, labels);
  119 +
  120 + CascadeBoostParams params(type, minTAR, maxFAR, trimRate, maxDepth, maxWeakCount);
  121 +
  122 + FeatureEvaluator featureEvaluator;
  123 + featureEvaluator.init(representation, images.size(), representation->numChannels());
  124 +
  125 + for (int i = 0; i < images.size(); i++)
  126 + featureEvaluator.setImage(images[i], labels[i], i);
  127 +
  128 + CascadeBoost boost;
  129 + boost.train(&featureEvaluator, images.size(), 1024, 1024, representation->numChannels(), params);
  130 +
  131 + threshold = boost.getThreshold();
  132 +
  133 + foreach (const CvBoostTree *classifier, boost.getClassifers()) {
  134 + Node *root = new Node;
  135 + buildTreeRecursive(root, classifier->get_root(), representation->maxCatCount());
  136 + classifiers.append(root);
  137 + }
  138 + }
  139 +
  140 + float classify(const Mat &image, bool process, float *confidence) const
  141 + {
  142 + Mat m;
  143 + if (process)
  144 + m = preprocess(image);
  145 + else
  146 + m = image;
  147 +
  148 + float sum = 0;
  149 + for (int i = 0; i < classifiers.size(); i++) {
  150 + Node *node = classifiers[i];
  151 +
  152 + while (node->left) {
  153 + if (representation->maxCatCount() > 0) {
  154 + int c = (int)representation->evaluate(m, node->featureIdx);
  155 + node = (node->subset[c >> 5] & (1 << (c & 31))) ? node->left : node->right;
  156 + } else {
  157 + double val = representation->evaluate(m, node->featureIdx);
  158 + node = val <= node->threshold ? node->left : node->right;
  159 + }
  160 + }
  161 +
  162 + sum += node->value;
  163 + }
  164 +
  165 + if (confidence)
  166 + *confidence = sum;
  167 + return sum < threshold - THRESHOLD_EPS ? 0.0f : 1.0f;
  168 + }
  169 +
  170 + int numFeatures() const
  171 + {
  172 + return representation->numFeatures();
  173 + }
  174 +
  175 + int maxCatCount() const
  176 + {
  177 + return representation->maxCatCount();
  178 + }
  179 +
  180 + Mat preprocess(const Mat &image) const
  181 + {
  182 + Mat dst;
  183 + representation->preprocess(image, dst);
  184 + return dst;
  185 + }
  186 +
  187 + Size windowSize(int *dx, int *dy) const
  188 + {
  189 + return representation->windowSize(dx, dy);
  190 + }
  191 +
  192 + void load(QDataStream &stream)
  193 + {
  194 + representation->load(stream);
  195 +
  196 + stream >> threshold;
  197 + int numClassifiers; stream >> numClassifiers;
  198 + for (int i = 0; i < numClassifiers; i++) {
  199 + Node *classifier = new Node;
  200 + loadRecursive(stream, classifier, representation->maxCatCount());
  201 + classifiers.append(classifier);
  202 + }
  203 + }
  204 +
  205 + void store(QDataStream &stream) const
  206 + {
  207 + representation->store(stream);
  208 +
  209 + stream << threshold;
  210 + stream << classifiers.size();
  211 + foreach (const Node *classifier, classifiers)
  212 + storeRecursive(stream, classifier, representation->maxCatCount());
  213 + }
  214 +};
  215 +
  216 +BR_REGISTER(Classifier, BoostedForestClassifier)
  217 +
  218 +} // namespace br
  219 +
  220 +#include "classification/boostedforest.moc"
... ...
openbr/plugins/classification/cascade.cpp 0 → 100644
  1 +#include <opencv2/imgproc/imgproc.hpp>
  2 +
  3 +#include <openbr/plugins/openbr_internal.h>
  4 +
  5 +using namespace cv;
  6 +
  7 +namespace br
  8 +{
  9 +
  10 +struct ImageHandler
  11 +{
  12 + bool create(const QList<Mat> &_posImages, const QList<Mat> &_negImages, Size _winSize)
  13 + {
  14 + posImages = _posImages;
  15 + negImages = _negImages;
  16 + winSize = _winSize;
  17 +
  18 + posIdx = negIdx = 0;
  19 +
  20 + src.create( 0, 0 , CV_8UC1 );
  21 + img.create( 0, 0, CV_8UC1 );
  22 + point = offset = Point( 0, 0 );
  23 + scale = 1.0F;
  24 + scaleFactor = 1.4142135623730950488016887242097F;
  25 + stepFactor = 0.5F;
  26 + round = 0;
  27 +
  28 + return true;
  29 + }
  30 +
  31 + void restart() { posIdx = 0; }
  32 +
  33 + void nextNeg()
  34 + {
  35 + int count = negImages.size();
  36 + for (int i = 0; i < count; i++) {
  37 + src = negImages[negIdx++];
  38 +
  39 + round += negIdx / count;
  40 + round = round % (winSize.width * winSize.height);
  41 + negIdx %= count;
  42 +
  43 + offset.x = qMin( (int)round % winSize.width, src.cols - winSize.width );
  44 + offset.y = qMin( (int)round / winSize.width, src.rows - winSize.height );
  45 + if (!src.empty() && src.type() == CV_8UC1 && offset.x >= 0 && offset.y >= 0)
  46 + break;
  47 + }
  48 +
  49 + point = offset;
  50 + scale = max(((float)winSize.width + point.x) / ((float)src.cols),
  51 + ((float)winSize.height + point.y) / ((float)src.rows));
  52 +
  53 + Size sz((int)(scale*src.cols + 0.5F), (int)(scale*src.rows + 0.5F));
  54 + resize(src, img, sz);
  55 + }
  56 +
  57 + bool getNeg(Mat &_img)
  58 + {
  59 + if (img.empty())
  60 + nextNeg();
  61 +
  62 + Mat m(winSize.height, winSize.width, CV_8UC1, (void*)(img.data + point.y * img.step + point.x * img.elemSize()), img.step);
  63 + m.copyTo(_img);
  64 +
  65 + if ((int)(point.x + (1.0F + stepFactor) * winSize.width) < img.cols)
  66 + point.x += (int)(stepFactor * winSize.width);
  67 + else {
  68 + point.x = offset.x;
  69 + if ((int)( point.y + (1.0F + stepFactor ) * winSize.height ) < img.rows)
  70 + point.y += (int)(stepFactor * winSize.height);
  71 + else {
  72 + point.y = offset.y;
  73 + scale *= scaleFactor;
  74 + if (scale <= 1.0F)
  75 + resize(src, img, Size((int)(scale*src.cols), (int)(scale*src.rows)));
  76 + else
  77 + nextNeg();
  78 + }
  79 + }
  80 + return true;
  81 + }
  82 +
  83 + bool getPos(Mat &_img)
  84 + {
  85 + if (posIdx >= posImages.size())
  86 + return false;
  87 +
  88 + posImages[posIdx++].copyTo(_img);
  89 + return true;
  90 + }
  91 +
  92 + QList<Mat> posImages, negImages;
  93 +
  94 + int posIdx, negIdx;
  95 +
  96 + Mat src, img;
  97 + Point offset, point;
  98 + float scale;
  99 + float scaleFactor;
  100 + float stepFactor;
  101 + size_t round;
  102 + Size winSize;
  103 +};
  104 +
  105 +class CascadeClassifier : public Classifier
  106 +{
  107 + Q_OBJECT
  108 +
  109 + Q_PROPERTY(QString stageDescription READ get_stageDescription WRITE set_stageDescription RESET reset_stageDescription STORED false)
  110 + Q_PROPERTY(int numStages READ get_numStages WRITE set_numStages RESET reset_numStages STORED false)
  111 + Q_PROPERTY(int numPos READ get_numPos WRITE set_numPos RESET reset_numPos STORED false)
  112 + Q_PROPERTY(int numNegs READ get_numNegs WRITE set_numNegs RESET reset_numNegs STORED false)
  113 + Q_PROPERTY(float maxFAR READ get_maxFAR WRITE set_maxFAR RESET reset_maxFAR STORED false)
  114 +
  115 + BR_PROPERTY(QString, stageDescription, "")
  116 + BR_PROPERTY(int, numStages, 20)
  117 + BR_PROPERTY(int, numPos, 1000)
  118 + BR_PROPERTY(int, numNegs, 1000)
  119 + BR_PROPERTY(float, maxFAR, pow(0.5, numStages))
  120 +
  121 + QList<Classifier *> stages;
  122 +
  123 + void train(const QList<Mat> &images, const QList<float> &labels)
  124 + {
  125 + QList<Mat> posImages, negImages;
  126 + for (int i = 0; i < images.size(); i++)
  127 + labels[i] == 1 ? posImages.append(images[i]) : negImages.append(images[i]);
  128 +
  129 + ImageHandler imgHandler;
  130 + imgHandler.create(posImages, negImages, Size(24, 24));
  131 +
  132 + stages.reserve(numStages);
  133 + for (int i = 0; i < numStages; i++) {
  134 + qDebug() << "===== TRAINING" << i << "stage =====";
  135 + qDebug() << "<BEGIN";
  136 +
  137 + QList<Mat> trainingImages;
  138 + QList<float> trainingLabels;
  139 +
  140 + float currFAR = fillTrainingSet(imgHandler, trainingImages, trainingLabels);
  141 +
  142 + if (currFAR < maxFAR) {
  143 + qDebug() << "FAR is below required level! Terminating early";
  144 + return;
  145 + }
  146 +
  147 + Classifier *next_stage = Classifier::make(stageDescription, NULL);
  148 + next_stage->train(trainingImages, trainingLabels);
  149 + stages.append(next_stage);
  150 +
  151 + qDebug() << "END>";
  152 + }
  153 + }
  154 +
  155 + float classify(const Mat &image, bool process, float *confidence) const
  156 + {
  157 + float stageConf = 0.0f;
  158 + foreach (const Classifier *stage, stages) {
  159 + float result = stage->classify(image, process, &stageConf);
  160 + if (confidence)
  161 + *confidence += stageConf;
  162 + if (result == 0.0f)
  163 + return 0.0f;
  164 + }
  165 + return 1.0f;
  166 + }
  167 +
  168 + int numFeatures() const
  169 + {
  170 + return stages.first()->numFeatures();
  171 + }
  172 +
  173 + int maxCatCount() const
  174 + {
  175 + return stages.first()->maxCatCount();
  176 + }
  177 +
  178 + Mat preprocess(const Mat &image) const
  179 + {
  180 + return stages.first()->preprocess(image);
  181 + }
  182 +
  183 + Size windowSize(int *dx, int *dy) const
  184 + {
  185 + return stages.first()->windowSize(dx, dy);
  186 + }
  187 +
  188 + void load(QDataStream &stream)
  189 + {
  190 + int numStages; stream >> numStages;
  191 + for (int i = 0; i < numStages; i++) {
  192 + Classifier *nextStage = Classifier::make(stageDescription, NULL);
  193 + nextStage->load(stream);
  194 + stages.append(nextStage);
  195 + }
  196 + }
  197 +
  198 + void store(QDataStream &stream) const
  199 + {
  200 + stream << stages.size();
  201 + foreach (const Classifier *stage, stages)
  202 + stage->store(stream);
  203 + }
  204 +
  205 +private:
  206 + float fillTrainingSet(ImageHandler &imgHandler, QList<Mat> &images, QList<float> &labels)
  207 + {
  208 + imgHandler.restart();
  209 +
  210 + float confidence = 0.0f;
  211 +
  212 + while (images.size() < numPos) {
  213 + Mat pos(imgHandler.winSize, CV_8UC1);
  214 + if (!imgHandler.getPos(pos))
  215 + qFatal("Cannot get another positive sample!");
  216 +
  217 + if (classify(pos, true, &confidence) > 0.0f) {
  218 + printf("POS current samples: %d\r", images.size());
  219 + images.append(pos);
  220 + labels.append(1.0f);
  221 + }
  222 + }
  223 +
  224 + int posCount = images.size();
  225 + qDebug() << "POS count : consumed " << posCount << ":" << imgHandler.posIdx;
  226 +
  227 + int passedNegs = 0;
  228 + while ((images.size() - posCount) < numNegs) {
  229 + Mat neg(imgHandler.winSize, CV_8UC1);
  230 + if (!imgHandler.getNeg(neg))
  231 + qFatal("Cannot get another negative sample!");
  232 +
  233 + if (classify(neg, true, &confidence) > 0.0f) {
  234 + printf("NEG current samples: %d\r", images.size() - posCount);
  235 + images.append(neg);
  236 + labels.append(0.0f);
  237 + }
  238 + passedNegs++;
  239 + }
  240 +
  241 + double acceptanceRatio = (images.size() - posCount) / (double)passedNegs;
  242 + qDebug() << "NEG count : acceptanceRatio " << images.size() - posCount << ":" << acceptanceRatio;
  243 + return acceptanceRatio;
  244 + }
  245 +};
  246 +
  247 +BR_REGISTER(Classifier, CascadeClassifier)
  248 +
  249 +} // namespace br
  250 +
  251 +#include "classification/cascade.moc"
... ...
openbr/plugins/gallery/vec.cpp 0 → 100644
  1 +#include <openbr/plugins/openbr_internal.h>
  2 +
  3 +namespace br
  4 +{
  5 +
  6 +/*!
  7 + * \ingroup galleries
  8 + * \brief Reads/writes OpenCV's .vec format.
  9 + * \author Scott Klum \cite sklum
  10 + */
  11 +
  12 +class vecGallery : public FileGallery
  13 +{
  14 + Q_OBJECT
  15 +
  16 + Q_PROPERTY(int width READ get_width WRITE set_width RESET reset_width STORED false)
  17 + Q_PROPERTY(int height READ get_height WRITE set_height RESET reset_height STORED false)
  18 + BR_PROPERTY(int, width, 24)
  19 + BR_PROPERTY(int, height, 24)
  20 +
  21 + QList<cv::Mat> mats;
  22 +
  23 + ~vecGallery()
  24 + {
  25 + if (mats.isEmpty())
  26 + return;
  27 +
  28 + writeOpen();
  29 +
  30 + // Write header
  31 + int count = mats.size();
  32 + int size = width*height;
  33 + short temp = 0;
  34 +
  35 + const size_t write1 = f.write((char*)&count,sizeof(count));
  36 + const size_t write2 = f.write((char*)&size,sizeof(size));
  37 + const size_t write3 = f.write((char*)&temp,sizeof(temp));
  38 + const size_t write4 = f.write((char*)&temp,sizeof(temp));
  39 +
  40 + if (write1 != sizeof(count) || write2 != sizeof(size) || write3 != sizeof(temp) || write4 != sizeof(temp))
  41 + qFatal("Failed to write header.");
  42 +
  43 + for (int i=0; i<count; i++) {
  44 + uchar tmp = 0;
  45 + const size_t write5 = f.write((char*)&tmp,sizeof(tmp));
  46 +
  47 + for (int r = 0; r < height; r++)
  48 + for (int c = 0; c < width; c++) {
  49 + short buffer = mats[i].ptr(r)[c];
  50 + f.write((char*)&buffer, sizeof(buffer));
  51 + }
  52 + }
  53 +
  54 + f.close();
  55 + }
  56 +
  57 + TemplateList readBlock(bool *done)
  58 + {
  59 + readOpen();
  60 +
  61 + *done = true;
  62 +
  63 + // Read header
  64 + int count, size;
  65 + short temp;
  66 +
  67 + const size_t read1 = f.read((char*)&count,sizeof(count));
  68 + const size_t read2 = f.read((char*)&size,sizeof(size));
  69 + const size_t read3 = f.read((char*)&temp,sizeof(temp));
  70 + const size_t read4 = f.read((char*)&temp,sizeof(temp));
  71 +
  72 + if (read1 != sizeof(count) || read2 != sizeof(size) || read3 != sizeof(temp) || read4 != sizeof(temp))
  73 + qFatal("Failed to read header.");
  74 +
  75 + if (size != width*height)
  76 + qFatal("Width*height != vector size.");
  77 +
  78 + // Read content
  79 + short *vec = new short[size];
  80 +
  81 + TemplateList templates;
  82 + for (int i=0; i<count; i++) {
  83 + uchar tmp = 0;
  84 + const size_t read5 = f.read((char*)&tmp,sizeof(tmp));
  85 + const size_t read6 = f.read((char*)vec,size*sizeof(short));
  86 +
  87 + if (read5 != sizeof(tmp) || read6 != size*sizeof(short))
  88 + qFatal("Unable to read vector.");
  89 +
  90 + cv::Mat m(height, width, CV_8UC1);
  91 + for (int r = 0; r < height; r++)
  92 + for (int c = 0; c < width; c++)
  93 + m.ptr(r)[c] = (uchar)vec[r*width+c];
  94 + Template t(m);
  95 + t.file.set("Label",1);
  96 + templates.append(t);
  97 + }
  98 +
  99 + return templates;
  100 + }
  101 +
  102 + void write(const Template &t)
  103 + {
  104 + if (t.m().rows == height && t.m().cols == width)
  105 + mats.append(t);
  106 + else
  107 + qFatal("Matrix has incorrect width/height.");
  108 + }
  109 +};
  110 +
  111 +BR_REGISTER(Gallery, vecGallery)
  112 +
  113 +} // namespace br
  114 +
  115 +#include "gallery/vec.moc"
  116 +
... ...
openbr/plugins/imgproc/affine.cpp
... ... @@ -109,6 +109,9 @@ private:
109 109  
110 110 Mat affineTransform = getAffineTransform(srcPoints, dstPoints);
111 111 warpAffine(src, dst, affineTransform, Size(width, height), method);
  112 + dst.file.set("Affine_0", OpenCVUtils::fromPoint(dstPoints[0]));
  113 + dst.file.set("Affine_1", OpenCVUtils::fromPoint(dstPoints[1]));
  114 + if (!twoPoints) dst.file.set("Affine_2", OpenCVUtils::fromPoint(dstPoints[2]));
112 115 if (storeAffine) {
113 116 QList<float> affineParams;
114 117 for (int i = 0 ; i < 2; i++)
... ...
openbr/plugins/imgproc/resizefilter.cpp 0 → 100644
  1 +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  2 + * Copyright 2012 The MITRE Corporation *
  3 + * *
  4 + * Licensed under the Apache License, Version 2.0 (the "License"); *
  5 + * you may not use this file except in compliance with the License. *
  6 + * You may obtain a copy of the License at *
  7 + * *
  8 + * http://www.apache.org/licenses/LICENSE-2.0 *
  9 + * *
  10 + * Unless required by applicable law or agreed to in writing, software *
  11 + * distributed under the License is distributed on an "AS IS" BASIS, *
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
  13 + * See the License for the specific language governing permissions and *
  14 + * limitations under the License. *
  15 + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  16 +
  17 +#include <opencv2/imgproc/imgproc.hpp>
  18 +
  19 +#include <openbr/plugins/openbr_internal.h>
  20 +
  21 +using namespace cv;
  22 +
  23 +namespace br
  24 +{
  25 +
  26 +/*!
  27 + * \ingroup transforms
  28 + * \brief Resize the template depending on its metadata
  29 + * \author Jordan Cheney \cite JordanCheney
  30 + * \note Method: Area should be used for shrinking an image, Cubic for slow but accurate enlargment, Bilin for fast enlargement.
  31 + */
  32 +class ResizeFilterTransform : public UntrainableTransform
  33 +{
  34 + Q_OBJECT
  35 + Q_ENUMS(Method)
  36 +
  37 +public:
  38 + /*!< */
  39 + enum Method { Near = INTER_NEAREST,
  40 + Area = INTER_AREA,
  41 + Bilin = INTER_LINEAR,
  42 + Cubic = INTER_CUBIC,
  43 + Lanczo = INTER_LANCZOS4};
  44 +
  45 +private:
  46 + Q_PROPERTY(int rows READ get_rows WRITE set_rows RESET reset_rows STORED false)
  47 + Q_PROPERTY(int columns READ get_columns WRITE set_columns RESET reset_columns STORED false)
  48 + Q_PROPERTY(Method method READ get_method WRITE set_method RESET reset_method STORED false)
  49 + Q_PROPERTY(QString filterKey READ get_filterKey WRITE set_filterKey RESET reset_filterKey STORED false)
  50 + Q_PROPERTY(QString filterVal READ get_filterVal WRITE set_filterVal RESET reset_filterVal STORED false)
  51 + BR_PROPERTY(int, rows, -1)
  52 + BR_PROPERTY(int, columns, -1)
  53 + BR_PROPERTY(Method, method, Bilin)
  54 + BR_PROPERTY(QString, filterKey, "Label")
  55 + BR_PROPERTY(QString, filterVal, "1.0")
  56 +
  57 + void project(const Template &src, Template &dst) const
  58 + {
  59 + dst = src;
  60 + if (src.file.get<QString>(filterKey) == filterVal)
  61 + resize(src, dst, Size((columns == -1) ? src.m().cols*rows/src.m().rows : columns, rows), 0, 0, method);
  62 + }
  63 +};
  64 +
  65 +BR_REGISTER(Transform, ResizeFilterTransform)
  66 +
  67 +} // namespace br
  68 +
  69 +#include "imgproc/resizefilter.moc"
... ...
openbr/plugins/imgproc/rndaffine.cpp 0 → 100644
  1 +#include <opencv2/imgproc/imgproc.hpp>
  2 +
  3 +#include <openbr/plugins/openbr_internal.h>
  4 +#include <openbr/core/opencvutils.h>
  5 +
  6 +using namespace cv;
  7 +
  8 +namespace br
  9 +{
  10 +
  11 +class RndAffineTransform : public UntrainableMetaTransform
  12 +{
  13 + Q_OBJECT
  14 + Q_PROPERTY(int numAffines READ get_numAffines WRITE set_numAffines RESET reset_numAffines STORED false)
  15 + Q_PROPERTY(int winWidth READ get_winWidth WRITE set_winWidth RESET reset_winWidth STORED false)
  16 + Q_PROPERTY(int winHeight READ get_winHeight WRITE set_winHeight RESET reset_winHeight STORED false)
  17 + Q_PROPERTY(float scaleFactor READ get_scaleFactor WRITE set_scaleFactor RESET reset_scaleFactor STORED false)
  18 + Q_PROPERTY(int maxAngle READ get_maxAngle WRITE set_maxAngle RESET reset_maxAngle STORED false)
  19 + BR_PROPERTY(int, numAffines, 0)
  20 + BR_PROPERTY(int, winWidth, 24)
  21 + BR_PROPERTY(int, winHeight, 24)
  22 + BR_PROPERTY(float, scaleFactor, 1.2)
  23 + BR_PROPERTY(int, maxAngle, 15)
  24 +
  25 + void project(const Template &src, Template &dst) const
  26 + {
  27 + TemplateList temp;
  28 + project(TemplateList() << src, temp);
  29 + if (!temp.isEmpty()) dst = temp.first();
  30 + }
  31 +
  32 + void project(const TemplateList &src, TemplateList &dst) const
  33 + {
  34 + foreach (const Template &t, src) {
  35 + QPointF affine_0 = t.file.get<QPointF>("Affine_0");
  36 + QPointF affine_1 = t.file.get<QPointF>("Affine_1");
  37 +
  38 + // Append the original points
  39 + Template u = t;
  40 + u.file.setPoints(QList<QPointF>() << affine_0 << affine_1);
  41 + u.file.set("Affine_0", affine_0);
  42 + u.file.set("Affine_1", affine_1);
  43 + dst.append(u);
  44 +
  45 + const double IPD = sqrt(pow(affine_0.x() - affine_1.x(), 2) + pow(affine_0.y() - affine_1.y(), 2));
  46 + for (int i = 0; i < numAffines; i++) {
  47 + int angle = (rand() % (2*maxAngle)) - maxAngle;
  48 +
  49 + int min = (int)(sqrt(1 / scaleFactor) * IPD);
  50 + int max = (int)(sqrt(scaleFactor) * IPD);
  51 + int dx = (rand() % (max - min)) + min;
  52 + int dy = (dx * sin(angle * M_PI / 180))/2;
  53 +
  54 + QPointF shiftedAffine_0 = QPointF(affine_1.x() - dx, affine_1.y() + dy);
  55 +
  56 + Template u = t;
  57 + u.file.setPoints(QList<QPointF>() << shiftedAffine_0 << affine_1);
  58 + u.file.set("Affine_0", shiftedAffine_0);
  59 + u.file.set("Affine_1", affine_1);
  60 + dst.append(u);
  61 + }
  62 + }
  63 + }
  64 +};
  65 +
  66 +BR_REGISTER(Transform, RndAffineTransform)
  67 +
  68 +} // namespace br
  69 +
  70 +#include "imgproc/rndaffine.moc"
... ...
openbr/plugins/imgproc/slidingwindow.cpp
... ... @@ -16,150 +16,150 @@
16 16  
17 17 #include <openbr/plugins/openbr_internal.h>
18 18 #include <openbr/core/opencvutils.h>
  19 +#include <openbr/core/qtutils.h>
  20 +
  21 +#include <opencv2/imgproc/imgproc.hpp>
19 22  
20 23 using namespace cv;
21 24  
22 25 namespace br
23 26 {
24 27  
25   -// Find avg aspect ratio
26   -static float getAspectRatio(const TemplateList &data)
27   -{
28   - double tempRatio = 0;
29   - int ratioCnt = 0;
30   -
31   - foreach (const Template &tmpl, data) {
32   - QList<Rect> posRects = OpenCVUtils::toRects(tmpl.file.rects());
33   - foreach (const Rect &posRect, posRects) {
34   - if (posRect.x + posRect.width >= tmpl.m().cols || posRect.y + posRect.height >= tmpl.m().rows || posRect.x < 0 || posRect.y < 0) {
35   - continue;
36   - }
37   - tempRatio += (float)posRect.width / (float)posRect.height;
38   - ratioCnt += 1;
39   - }
40   - }
41   - return tempRatio / (double)ratioCnt;
42   -}
43   -
44 28 /*!
45 29 * \ingroup transforms
46   - * \brief Applies a transform to a sliding window.
47   - * Discards negative detections.
48   - * \author Austin Blanton \cite imaus10
  30 + * \brief Sliding Window Framework
  31 + * \author Jordan Cheney
49 32 */
50   -class SlidingWindowTransform : public Transform
  33 +
  34 +class SlidingWindowTransform : public MetaTransform
51 35 {
52 36 Q_OBJECT
53   - Q_PROPERTY(br::Transform *transform READ get_transform WRITE set_transform RESET reset_transform STORED false)
54   - Q_PROPERTY(int windowWidth READ get_windowWidth WRITE set_windowWidth RESET reset_windowWidth STORED false)
55   - Q_PROPERTY(bool takeFirst READ get_takeFirst WRITE set_takeFirst RESET reset_takeFirst STORED false)
56   - Q_PROPERTY(float threshold READ get_threshold WRITE set_threshold RESET reset_threshold STORED false)
57   - Q_PROPERTY(float stepFraction READ get_stepFraction WRITE set_stepFraction RESET reset_stepFraction STORED false)
58   - Q_PROPERTY(int ignoreBorder READ get_ignoreBorder WRITE set_ignoreBorder RESET reset_ignoreBorder STORED true)
59   - BR_PROPERTY(br::Transform *, transform, NULL)
60   - BR_PROPERTY(int, windowWidth, 24)
61   - BR_PROPERTY(bool, takeFirst, false)
62   - BR_PROPERTY(float, threshold, 0)
63   - BR_PROPERTY(float, stepFraction, 0.25)
64   - BR_PROPERTY(int, ignoreBorder, 0)
65   -
66   -private:
67   - int windowHeight;
68   - bool skipProject;
  37 +
  38 + Q_PROPERTY(br::Classifier* classifier READ get_classifier WRITE set_classifier RESET reset_classifier STORED false)
  39 +
  40 + Q_PROPERTY(int minSize READ get_minSize WRITE set_minSize RESET reset_minSize STORED false)
  41 + Q_PROPERTY(int maxSize READ get_maxSize WRITE set_maxSize RESET reset_maxSize STORED false)
  42 + Q_PROPERTY(float scaleFactor READ get_scaleFactor WRITE set_scaleFactor RESET reset_scaleFactor STORED false)
  43 + Q_PROPERTY(int minNeighbors READ get_minNeighbors WRITE set_minNeighbors RESET reset_minNeighbors STORED false)
  44 + Q_PROPERTY(float confidenceThreshold READ get_confidenceThreshold WRITE set_confidenceThreshold RESET reset_confidenceThreshold STORED false)
  45 + Q_PROPERTY(float eps READ get_eps WRITE set_eps RESET reset_eps STORED false)
  46 +
  47 + BR_PROPERTY(br::Classifier*, classifier, NULL)
  48 + BR_PROPERTY(int, minSize, 20)
  49 + BR_PROPERTY(int, maxSize, -1)
  50 + BR_PROPERTY(float, scaleFactor, 1.2)
  51 + BR_PROPERTY(int, minNeighbors, 5)
  52 + BR_PROPERTY(float, confidenceThreshold, 10)
  53 + BR_PROPERTY(float, eps, 0.2)
69 54  
70 55 void train(const TemplateList &data)
71 56 {
72   - skipProject = true;
73   - float aspectRatio = data.first().file.get<float>("aspectRatio", -1);
74   - if (aspectRatio == -1)
75   - aspectRatio = getAspectRatio(data);
76   - windowHeight = qRound(windowWidth / aspectRatio);
77   -
78   - if (transform->trainable) {
79   - TemplateList dataOut = data;
80   - if (ignoreBorder > 0) {
81   - for (int i = 0; i < dataOut.size(); i++) {
82   - Template t = dataOut[i];
83   - Mat m = t.m();
84   - dataOut.replace(i,Template(t.file, Mat(m,Rect(ignoreBorder,ignoreBorder,m.cols - ignoreBorder * 2, m.rows - ignoreBorder * 2))));
85   - }
86   - }
87   - transform->train(dataOut);
88   - }
  57 + classifier->train(data.data(), File::get<float>(data, "Label", -1));
89 58 }
90 59  
91   - void store(QDataStream &stream) const
  60 + void project(const Template &src, Template &dst) const
92 61 {
93   - transform->store(stream);
94   - stream << windowHeight;
  62 + TemplateList temp;
  63 + project(TemplateList() << src, temp);
  64 + if (!temp.isEmpty()) dst = temp.first();
95 65 }
96 66  
97   - void load(QDataStream &stream)
  67 + void project(const TemplateList &src, TemplateList &dst) const
98 68 {
99   - transform->load(stream);
100   - stream >> windowHeight;
101   - }
  69 + Size minObjectSize(minSize, minSize);
  70 + Size maxObjectSize;
102 71  
103   - void project(const Template &src, Template &dst) const
104   - {
105   - float scale = src.file.get<float>("scale", 1);
106   - projectHelp(src, dst, windowWidth, windowHeight, scale);
107   - }
  72 + foreach (const Template &t, src) {
  73 + const bool enrollAll = t.file.getBool("enrollAll");
  74 +
  75 + // Mirror the behavior of ExpandTransform in the special case
  76 + // of an empty template.
  77 + if (t.empty() && !enrollAll) {
  78 + dst.append(t);
  79 + continue;
  80 + }
108 81  
109   - protected:
110   - void projectHelp(const Template &src, Template &dst, int windowWidth, int windowHeight, float scale = 1) const
111   - {
  82 + for (int i=0; i<t.size(); i++) {
  83 + Mat m;
  84 + OpenCVUtils::cvtUChar(t[i], m);
  85 + std::vector<Rect> rects;
  86 + std::vector<float> confidences;
112 87  
113   - dst = src;
114   - if (skipProject) {
115   - dst = src;
116   - return;
117   - }
  88 + if (maxObjectSize.height == 0 || maxObjectSize.width == 0)
  89 + maxObjectSize = m.size();
  90 +
  91 + Mat imageBuffer(m.rows + 1, m.cols + 1, CV_8U);
118 92  
119   - Template windowTemplate(src.file, src);
120   - QList<float> confidences = dst.file.getList<float>("Confidences", QList<float>());
121   - for (float y = 0; y + windowHeight < src.m().rows; y += windowHeight*stepFraction) {
122   - for (float x = 0; x + windowWidth < src.m().cols; x += windowWidth*stepFraction) {
123   - Mat windowMat(src, Rect(x + ignoreBorder, y + ignoreBorder, windowWidth - ignoreBorder * 2, windowHeight - ignoreBorder * 2));
124   - windowTemplate.replace(0,windowMat);
125   - Template detect;
126   - transform->project(windowTemplate, detect);
127   - float conf = detect.m().at<float>(0);
128   -
129   - // the result will be in the Label
130   - if (conf > threshold) {
131   - dst.file.appendRect(QRectF(x*scale, y*scale, windowWidth*scale, windowHeight*scale));
132   - confidences.append(conf);
133   - if (takeFirst)
134   - return;
  93 + for (double factor = 1; ; factor *= scaleFactor) {
  94 + int dx, dy;
  95 + Size originalWindowSize = classifier->windowSize(&dx, &dy);
  96 +
  97 + Size windowSize(cvRound(originalWindowSize.width*factor), cvRound(originalWindowSize.height*factor) );
  98 + Size scaledImageSize(cvRound(m.cols/factor ), cvRound(m.rows/factor));
  99 + Size processingRectSize(scaledImageSize.width - originalWindowSize.width, scaledImageSize.height - originalWindowSize.height);
  100 +
  101 + if (processingRectSize.width <= 0 || processingRectSize.height <= 0)
  102 + break;
  103 + if (windowSize.width > maxObjectSize.width || windowSize.height > maxObjectSize.height)
  104 + break;
  105 + if (windowSize.width < minObjectSize.width || windowSize.height < minObjectSize.height)
  106 + continue;
  107 +
  108 + Mat scaledImage(scaledImageSize, CV_8U, imageBuffer.data);
  109 + resize(m, scaledImage, scaledImageSize, 0, 0, CV_INTER_LINEAR);
  110 +
  111 + Mat repImage = classifier->preprocess(scaledImage);
  112 +
  113 + int step = factor > 2. ? 1 : 2;
  114 + for (int y = 0; y < processingRectSize.height; y += step) {
  115 + for (int x = 0; x < processingRectSize.width; x += step) {
  116 + Mat window = repImage(Rect(Point(x, y), Size(originalWindowSize.width + dx, originalWindowSize.height + dy))).clone();
  117 +
  118 + float confidence = 0;
  119 + int result = classifier->classify(window, false, &confidence);
  120 +
  121 + if (result == 1) {
  122 + rects.push_back(Rect(cvRound(x*factor), cvRound(y*factor), windowSize.width, windowSize.height));
  123 + confidences.push_back(confidence);
  124 + }
  125 +
  126 + // TODO: Add non ROC mode
  127 +
  128 + if (result == 0)
  129 + x += step;
  130 + }
  131 + }
  132 + }
  133 +
  134 + OpenCVUtils::group(rects, confidences, confidenceThreshold, eps);
  135 +
  136 + if (!enrollAll && rects.empty())
  137 + rects.push_back(Rect(0, 0, m.cols, m.rows));
  138 +
  139 + for (size_t j=0; j<rects.size(); j++) {
  140 + Template u(t.file, m);
  141 + u.file.set("Confidence", confidences[j]);
  142 + const QRectF rect = OpenCVUtils::fromRect(rects[j]);
  143 + u.file.appendRect(rect);
  144 + u.file.set("Face", rect);
  145 + dst.append(u);
135 146 }
136 147 }
137 148 }
138   - dst.file.setList<float>("Confidences", confidences);
139 149 }
140   -};
141 150  
142   -BR_REGISTER(Transform, SlidingWindowTransform)
143   -
144   -/*!
145   - * \ingroup transforms
146   - * \brief Overloads SlidingWindowTransform for integral images that should be
147   - * sampled at multiple scales.
148   - * \author Josh Klontz \cite jklontz
149   - */
150   -class IntegralSlidingWindowTransform : public SlidingWindowTransform
151   -{
152   - Q_OBJECT
  151 + void load(QDataStream &stream)
  152 + {
  153 + classifier->load(stream);
  154 + }
153 155  
154   - private:
155   - void project(const Template &src, Template &dst) const
  156 + void store(QDataStream &stream) const
156 157 {
157   - // TODO: call SlidingWindowTransform::project on multiple scales
158   - SlidingWindowTransform::projectHelp(src, dst, 24, 24);
  158 + classifier->store(stream);
159 159 }
160 160 };
161 161  
162   -BR_REGISTER(Transform, IntegralSlidingWindowTransform)
  162 +BR_REGISTER(Transform, SlidingWindowTransform)
163 163  
164 164 } // namespace br
165 165  
... ...
openbr/plugins/io/read.cpp
... ... @@ -15,7 +15,7 @@
15 15 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16 16  
17 17 #include <opencv2/highgui/highgui.hpp>
18   -
  18 +#include <openbr/core/opencvutils.h>
19 19 #include <openbr/plugins/openbr_internal.h>
20 20  
21 21 using namespace cv;
... ... @@ -59,9 +59,13 @@ private:
59 59 else dst.file.fte = true;
60 60 } else {
61 61 foreach (const Mat &m, src) {
62   - const Mat img = imdecode(m, mode);
63   - if (img.data) dst.append(img);
64   - else dst.file.fte = true;
  62 + if (((m.rows > 1) && (m.cols > 1)) || (m.type() != CV_8UC1))
  63 + dst += m;
  64 + else {
  65 + const Mat img = imdecode(m, mode);
  66 + if (img.data) dst.append(img);
  67 + else dst.file.fte = true;
  68 + }
65 69 }
66 70 }
67 71 if (dst.file.fte)
... ...
openbr/plugins/io/write.cpp
... ... @@ -31,10 +31,10 @@ class WriteTransform : public TimeVaryingTransform
31 31 {
32 32 Q_OBJECT
33 33 Q_PROPERTY(QString outputDirectory READ get_outputDirectory WRITE set_outputDirectory RESET reset_outputDirectory STORED false)
34   - Q_PROPERTY(QString imageName READ get_imageName WRITE set_imageName RESET reset_imageName STORED false)
  34 + Q_PROPERTY(QString underscore READ get_underscore WRITE set_underscore RESET reset_underscore STORED false)
35 35 Q_PROPERTY(QString imgExtension READ get_imgExtension WRITE set_imgExtension RESET reset_imgExtension STORED false)
36 36 BR_PROPERTY(QString, outputDirectory, "Temp")
37   - BR_PROPERTY(QString, imageName, "image")
  37 + BR_PROPERTY(QString, underscore, "")
38 38 BR_PROPERTY(QString, imgExtension, "jpg")
39 39  
40 40 int cnt;
... ... @@ -48,7 +48,8 @@ class WriteTransform : public TimeVaryingTransform
48 48 void projectUpdate(const Template &src, Template &dst)
49 49 {
50 50 dst = src;
51   - OpenCVUtils::saveImage(dst.m(), QString("%1/%2_%3.%4").arg(outputDirectory).arg(imageName).arg(cnt++, 5, 10, QChar('0')).arg(imgExtension));
  51 + QString path = QString("%1/image%2%3.%4").arg(outputDirectory).arg(cnt++, 5, 10, QChar('0')).arg(underscore.isEmpty() ? "" : "_" + underscore).arg(imgExtension);
  52 + OpenCVUtils::saveImage(dst.m(), path);
52 53 }
53 54  
54 55 };
... ...
openbr/plugins/metadata/registerpointsasaffine.cpp 0 → 100644
  1 +#include <openbr/plugins/openbr_internal.h>
  2 +
  3 +namespace br
  4 +{
  5 +
  6 +class RegisterPointsAsAffine : public UntrainableMetadataTransform
  7 +{
  8 + Q_OBJECT
  9 + Q_PROPERTY(QList<int> pointsIdxs READ get_pointIdxs WRITE set_pointIdxs RESET reset_pointIdxs STORED false)
  10 + BR_PROPERTY(QList<int>, pointIdxs, QList<int>())
  11 +
  12 + void projectMetadata(const File &src, File &dst) const
  13 + {
  14 + const int chin = 20;
  15 +
  16 + if (pointIdxs.size() != 2 && pointIdxs.size() != 3)
  17 + qFatal("Need 2 or 3 points for affine transform");
  18 +
  19 + dst = src;
  20 +
  21 + QList<QPointF> points = src.points();
  22 +
  23 + if (points[pointIdxs[0]] == QPointF(-1, -1) ||
  24 + points[pointIdxs[1]] == QPointF(-1, -1))
  25 + dst.fte = true;
  26 +
  27 + if (points[chin] == QPointF(-1, -1))
  28 + dst.fte = true;
  29 +
  30 + dst.set("Affine_0", points[pointIdxs[0]]);
  31 + dst.set("Affine_1", points[pointIdxs[1]]);
  32 + if (pointIdxs.size() == 3)
  33 + dst.set("Affine_2", points[pointIdxs[2]]);
  34 + }
  35 +};
  36 +
  37 +BR_REGISTER(Transform, RegisterPointsAsAffine)
  38 +
  39 +} // namespace br
  40 +
  41 +#include "metadata/registerpointsasaffine.moc"
... ...
openbr/plugins/representation/gradienthistogram.cpp 0 → 100644
  1 +#include <opencv2/imgproc/imgproc.hpp>
  2 +#include <opencv2/highgui/highgui.hpp>
  3 +
  4 +#include <openbr/plugins/openbr_internal.h>
  5 +#include <openbr/core/opencvutils.h>
  6 +
  7 +using namespace cv;
  8 +
  9 +namespace br
  10 +{
  11 +
  12 +/*!
  13 + * \ingroup galleries
  14 + * \brief Computes first order gradient histogram features using an integral image
  15 + * \author Scott Klum \cite sklum
  16 + */
  17 +class GradientHistogramRepresentation : public Representation
  18 +{
  19 + Q_OBJECT
  20 +
  21 + Q_PROPERTY(int winWidth READ get_winWidth WRITE set_winWidth RESET reset_winWidth STORED false)
  22 + Q_PROPERTY(int winHeight READ get_winHeight WRITE set_winHeight RESET reset_winHeight STORED false)
  23 + Q_PROPERTY(int bins READ get_bins WRITE set_bins RESET reset_bins STORED false)
  24 + BR_PROPERTY(int, winWidth, 24)
  25 + BR_PROPERTY(int, winHeight, 24)
  26 + BR_PROPERTY(int, bins, 6)
  27 +
  28 + void init()
  29 + {
  30 + int dx, dy;
  31 + Size size = windowSize(&dx,&dy);
  32 +
  33 + int width = size.width+dx, height = size.height+dy;
  34 +
  35 + // Enumerate all possible rectangles
  36 + for (int x=0; x<width; x++)
  37 + for (int y=0; y<height; y++)
  38 + for (int w=1; w <= width-x; w++)
  39 + for (int h=1; h <= height-y; h++)
  40 + features.append(Rect(x,y,w,h));
  41 + }
  42 +
  43 + void preprocess(const Mat &src, Mat &dst) const
  44 + {
  45 + // Compute as is done in GradientTransform
  46 + Mat dx, dy, magnitude, angle;
  47 + Sobel(src, dx, CV_32F, 1, 0, CV_SCHARR);
  48 + Sobel(src, dy, CV_32F, 0, 1, CV_SCHARR);
  49 + cartToPolar(dx, dy, magnitude, angle, true);
  50 +
  51 + const double floor = ((src.depth() == CV_32F) || (src.depth() == CV_64F)) ? -0.5 : 0;
  52 +
  53 + Mat histogram;
  54 + angle.convertTo(histogram, bins > 256 ? CV_16U : CV_8U, bins/360., floor);
  55 +
  56 + // Mask and compute integral image
  57 + std::vector<Mat> outputs;
  58 + for (int i=0; i<bins; i++) {
  59 + Mat output = (histogram == i)/255;
  60 + Mat integralImg;
  61 + integral(output, integralImg);
  62 + outputs.push_back(integralImg);
  63 + }
  64 +
  65 + // Concatenate images into row
  66 + merge(outputs,dst);
  67 + }
  68 +
  69 + /* ___ ___
  70 + * | | |
  71 + * | A | B |
  72 + * |___|___|
  73 + * | | |
  74 + * | C | D |
  75 + * |___|___|
  76 + *
  77 + * 1, 2, 3 and 4 refer to the lower right corners of A, B, C, and D, respectively.
  78 + * Rectangle D can be computed as 4 + 1 - (2 + 3)
  79 + */
  80 +
  81 + float evaluate(const Mat &image, int idx) const
  82 + {
  83 + // To which channel does idx belong?
  84 + const int index = idx % features.size();
  85 + const int channel = idx / features.size();
  86 +
  87 + int dx, dy;
  88 + Size size = windowSize(&dx, &dy);
  89 +
  90 + int four = image.ptr<int>(0)[((features[index].y+features[index].height)*(size.height+dy)+(features[index].x+features[index].width))*bins+channel];
  91 + int one = image.ptr<int>(0)[(features[index].y*(size.height+dy)+features[index].x)*bins+channel];
  92 + int two = image.ptr<int>(0)[(features[index].y*(size.height+dy)+(features[index].x+features[index].width))*bins+channel];
  93 + int three = image.ptr<int>(0)[(features[index].y+features[index].height*(size.height+dy)+features[index].x)*bins+channel];
  94 +
  95 + return four + one - (two + three);
  96 + }
  97 +
  98 + Mat evaluate(const Mat &image, const QList<int> &indices) const
  99 + {
  100 + int size = indices.empty() ? numFeatures() : indices.size();
  101 +
  102 + Mat result(1, size, CV_32FC1);
  103 + for (int i = 0; i < size; i++)
  104 + result.at<float>(i) = evaluate(image, indices.empty() ? i : indices[i]);
  105 + return result;
  106 + }
  107 +
  108 + int numFeatures() const
  109 + {
  110 + return features.size()*bins;
  111 + }
  112 +
  113 + int numChannels() const
  114 + {
  115 + return bins;
  116 + }
  117 +
  118 + Size windowSize(int *dx, int *dy) const
  119 + {
  120 + if (dx && dy)
  121 + *dx = *dy = 1;
  122 + return Size(winWidth, winHeight);
  123 + }
  124 +
  125 + int maxCatCount() const { return 0; }
  126 +
  127 + QList<Rect> features;
  128 +};
  129 +
  130 +BR_REGISTER(Representation, GradientHistogramRepresentation)
  131 +
  132 +} // namespace br
  133 +
  134 +#include "representation/gradienthistogram.moc"
  135 +
  136 +
... ...
openbr/plugins/representation/haar.cpp 0 → 100644
  1 +#include <opencv2/imgproc/imgproc.hpp>
  2 +
  3 +#include <openbr/plugins/openbr_internal.h>
  4 +#include <openbr/core/opencvutils.h>
  5 +
  6 +using namespace cv;
  7 +
  8 +namespace br
  9 +{
  10 +
  11 +#define CV_SUM_OFFSETS( p0, p1, p2, p3, rect, step ) \
  12 + /* (x, y) */ \
  13 + (p0) = (rect).x + (step) * (rect).y; \
  14 + /* (x + w, y) */ \
  15 + (p1) = (rect).x + (rect).width + (step) * (rect).y; \
  16 + /* (x + w, y) */ \
  17 + (p2) = (rect).x + (step) * ((rect).y + (rect).height); \
  18 + /* (x + w, y + h) */ \
  19 + (p3) = (rect).x + (rect).width + (step) * ((rect).y + (rect).height);
  20 +
  21 +class HaarRepresentation : public Representation
  22 +{
  23 + Q_OBJECT
  24 +
  25 + Q_PROPERTY(int winWidth READ get_winWidth WRITE set_winWidth RESET reset_winWidth STORED false)
  26 + Q_PROPERTY(int winHeight READ get_winHeight WRITE set_winHeight RESET reset_winHeight STORED false)
  27 + BR_PROPERTY(int, winWidth, 24)
  28 + BR_PROPERTY(int, winHeight, 24)
  29 +
  30 + void init()
  31 + {
  32 + int offset = winWidth + 1;
  33 + for (int x = 0; x < winWidth; x++) {
  34 + for (int y = 0; y < winHeight; y++) {
  35 + for (int dx = 1; dx <= winWidth; dx++) {
  36 + for (int dy = 1; dy <= winHeight; dy++) {
  37 + // haar_x2
  38 + if ((x+dx*2 <= winWidth) && (y+dy <= winHeight))
  39 + features.append(Feature(offset,
  40 + x, y, dx*2, dy, -1,
  41 + x+dx, y, dx , dy, +2));
  42 + // haar_y2
  43 + if ((x+dx <= winWidth) && (y+dy*2 <= winHeight))
  44 + features.append(Feature(offset,
  45 + x, y, dx, dy*2, -1,
  46 + x, y+dy, dx, dy, +2));
  47 + // haar_x3
  48 + if ((x+dx*3 <= winWidth) && (y+dy <= winHeight))
  49 + features.append(Feature(offset,
  50 + x, y, dx*3, dy, -1,
  51 + x+dx, y, dx , dy, +3));
  52 + // haar_y3
  53 + if ((x+dx <= winWidth) && (y+dy*3 <= winHeight))
  54 + features.append(Feature(offset,
  55 + x, y, dx, dy*3, -1,
  56 + x, y+dy, dx, dy, +3));
  57 + // x2_y2
  58 + if ((x+dx*2 <= winWidth) && (y+dy*2 <= winHeight))
  59 + features.append(Feature(offset,
  60 + x, y, dx*2, dy*2, -1,
  61 + x, y, dx, dy, +2,
  62 + x+dx, y+dy, dx, dy, +2));
  63 +
  64 +
  65 + }
  66 + }
  67 + }
  68 + }
  69 + }
  70 +
  71 + void preprocess(const Mat &src, Mat &dst) const
  72 + {
  73 + integral(src, dst);
  74 + }
  75 +
  76 + float evaluate(const Mat &image, int idx) const
  77 + {
  78 + return (float)features[idx].calc(image);
  79 + }
  80 +
  81 + Mat evaluate(const Mat &image, const QList<int> &indices) const
  82 + {
  83 + int size = indices.empty() ? numFeatures() : indices.size();
  84 +
  85 + Mat result(1, size, CV_32FC1);
  86 + for (int i = 0; i < size; i++)
  87 + result.at<float>(i) = evaluate(image, indices.empty() ? i : indices[i]);
  88 + return result;
  89 + }
  90 +
  91 + int numFeatures() const
  92 + {
  93 + return features.size();
  94 + }
  95 +
  96 + Size windowSize(int *dx, int *dy) const
  97 + {
  98 + if (dx && dy)
  99 + *dx = *dy = 1;
  100 + return Size(winWidth, winHeight);
  101 + }
  102 +
  103 + int maxCatCount() const { return 0; }
  104 +
  105 + struct Feature
  106 + {
  107 + Feature();
  108 + Feature( int offset,
  109 + int x0, int y0, int w0, int h0, float wt0,
  110 + int x1, int y1, int w1, int h1, float wt1,
  111 + int x2 = 0, int y2 = 0, int w2 = 0, int h2 = 0, float wt2 = 0.0F );
  112 + float calc(const Mat &img) const;
  113 +
  114 + struct {
  115 + Rect r;
  116 + float weight;
  117 + } rect[3];
  118 +
  119 + struct {
  120 + int p0, p1, p2, p3;
  121 + } fastRect[3];
  122 + };
  123 +
  124 + QList<Feature> features;
  125 +};
  126 +
  127 +BR_REGISTER(Representation, HaarRepresentation)
  128 +
  129 +HaarRepresentation::Feature::Feature()
  130 +{
  131 + rect[0].r = rect[1].r = rect[2].r = Rect(0,0,0,0);
  132 + rect[0].weight = rect[1].weight = rect[2].weight = 0;
  133 +}
  134 +
  135 +HaarRepresentation::Feature::Feature(int offset,
  136 + int x0, int y0, int w0, int h0, float wt0,
  137 + int x1, int y1, int w1, int h1, float wt1,
  138 + int x2, int y2, int w2, int h2, float wt2)
  139 +{
  140 + rect[0].r.x = x0;
  141 + rect[0].r.y = y0;
  142 + rect[0].r.width = w0;
  143 + rect[0].r.height = h0;
  144 + rect[0].weight = wt0;
  145 +
  146 + rect[1].r.x = x1;
  147 + rect[1].r.y = y1;
  148 + rect[1].r.width = w1;
  149 + rect[1].r.height = h1;
  150 + rect[1].weight = wt1;
  151 +
  152 + rect[2].r.x = x2;
  153 + rect[2].r.y = y2;
  154 + rect[2].r.width = w2;
  155 + rect[2].r.height = h2;
  156 + rect[2].weight = wt2;
  157 +
  158 + for (int j = 0; j < 3; j++) {
  159 + if( rect[j].weight == 0.0F )
  160 + break;
  161 + CV_SUM_OFFSETS(fastRect[j].p0, fastRect[j].p1, fastRect[j].p2, fastRect[j].p3, rect[j].r, offset)
  162 + }
  163 +}
  164 +
  165 +inline float HaarRepresentation::Feature::calc(const Mat &img) const
  166 +{
  167 + const int* ptr = img.ptr<int>();
  168 + float ret = rect[0].weight * (ptr[fastRect[0].p0] - ptr[fastRect[0].p1] - ptr[fastRect[0].p2] + ptr[fastRect[0].p3]) +
  169 + rect[1].weight * (ptr[fastRect[1].p0] - ptr[fastRect[1].p1] - ptr[fastRect[1].p2] + ptr[fastRect[1].p3]);
  170 + if (rect[2].weight != 0.0f)
  171 + ret += rect[2].weight * (ptr[fastRect[2].p0] - ptr[fastRect[2].p1] - ptr[fastRect[2].p2] + ptr[fastRect[2].p3]);
  172 + return ret;
  173 +}
  174 +
  175 +} // namespace br
  176 +
  177 +#include "representation/haar.moc"
  178 +
... ...
openbr/plugins/representation/mblbp.cpp 0 → 100644
  1 +#include <opencv2/imgproc/imgproc.hpp>
  2 +
  3 +#include <openbr/plugins/openbr_internal.h>
  4 +#include <openbr/core/opencvutils.h>
  5 +
  6 +using namespace cv;
  7 +
  8 +namespace br
  9 +{
  10 +
  11 +#define CV_SUM_OFFSETS( p0, p1, p2, p3, rect, step ) \
  12 + /* (x, y) */ \
  13 + (p0) = (rect).x + (step) * (rect).y; \
  14 + /* (x + w, y) */ \
  15 + (p1) = (rect).x + (rect).width + (step) * (rect).y; \
  16 + /* (x + w, y) */ \
  17 + (p2) = (rect).x + (step) * ((rect).y + (rect).height); \
  18 + /* (x + w, y + h) */ \
  19 + (p3) = (rect).x + (rect).width + (step) * ((rect).y + (rect).height);
  20 +
  21 +class MBLBPRepresentation : public Representation
  22 +{
  23 + Q_OBJECT
  24 +
  25 + Q_PROPERTY(int winWidth READ get_winWidth WRITE set_winWidth RESET reset_winWidth STORED false)
  26 + Q_PROPERTY(int winHeight READ get_winHeight WRITE set_winHeight RESET reset_winHeight STORED false)
  27 + BR_PROPERTY(int, winWidth, 24)
  28 + BR_PROPERTY(int, winHeight, 24)
  29 +
  30 + void init()
  31 + {
  32 + int offset = winWidth + 1;
  33 + for (int x = 0; x < winWidth; x++ )
  34 + for (int y = 0; y < winHeight; y++ )
  35 + for (int w = 1; w <= winWidth / 3; w++ )
  36 + for (int h = 1; h <= winHeight / 3; h++ )
  37 + if ((x+3*w <= winWidth) && (y+3*h <= winHeight) )
  38 + features.append(Feature(offset, x, y, w, h ) );
  39 + }
  40 +
  41 + void preprocess(const Mat &src, Mat &dst) const
  42 + {
  43 + integral(src, dst);
  44 + }
  45 +
  46 + float evaluate(const Mat &image, int idx) const
  47 + {
  48 + return (float)features[idx].calc(image);
  49 + }
  50 +
  51 + Mat evaluate(const Mat &image, const QList<int> &indices) const
  52 + {
  53 + int size = indices.empty() ? numFeatures() : indices.size();
  54 +
  55 + Mat result(1, size, CV_32FC1);
  56 + for (int i = 0; i < size; i++)
  57 + result.at<float>(i) = evaluate(image, indices.empty() ? i : indices[i]);
  58 + return result;
  59 + }
  60 +
  61 + Size windowSize(int *dx, int *dy) const
  62 + {
  63 + if (dx && dy)
  64 + *dx = *dy = 1;
  65 + return Size(winWidth, winHeight);
  66 + }
  67 +
  68 + int numFeatures() const { return features.size(); }
  69 + int maxCatCount() const { return 256; }
  70 +
  71 + struct Feature
  72 + {
  73 + Feature() { rect = Rect(0, 0, 0, 0); }
  74 + Feature( int offset, int x, int y, int _block_w, int _block_h );
  75 + uchar calc(const Mat &img) const;
  76 +
  77 + Rect rect;
  78 + int p[16];
  79 + };
  80 + QList<Feature> features;
  81 +};
  82 +
  83 +BR_REGISTER(Representation, MBLBPRepresentation)
  84 +
  85 +static inline void calcOffset(int &p0, int &p1, int &p2, int &p3, Rect rect, int offset)
  86 +{
  87 + /* (x, y) */
  88 + p0 = rect.x + offset * rect.y;
  89 + /* (x + w, y) */
  90 + p1 = rect.x + rect.width + offset * rect.y;
  91 + /* (x + w, y) */
  92 + p2 = rect.x + offset * (rect.y + rect.height);
  93 + /* (x + w, y + h) */
  94 + p3 = rect.x + rect.width + offset * (rect.y + rect.height);
  95 +}
  96 +
  97 +MBLBPRepresentation::Feature::Feature( int offset, int x, int y, int _blockWidth, int _blockHeight )
  98 +{
  99 + Rect tr = rect = cvRect(x, y, _blockWidth, _blockHeight);
  100 + calcOffset(p[0], p[1], p[4], p[5], tr, offset);
  101 + tr.x += 2*rect.width;
  102 + calcOffset(p[2], p[3], p[6], p[7], tr, offset);
  103 + tr.y +=2*rect.height;
  104 + calcOffset(p[10], p[11], p[14], p[15], tr, offset);
  105 + tr.x -= 2*rect.width;
  106 + calcOffset(p[8], p[9], p[12], p[13], tr, offset);
  107 +}
  108 +
  109 +inline uchar MBLBPRepresentation::Feature::calc(const Mat &img) const
  110 +{
  111 + const int* ptr = img.ptr<int>();
  112 + int cval = ptr[p[5]] - ptr[p[6]] - ptr[p[9]] + ptr[p[10]];
  113 +
  114 + return (uchar)((ptr[p[0]] - ptr[p[1]] - ptr[p[4]] + ptr[p[5]] >= cval ? 128 : 0) | // 0
  115 + (ptr[p[1]] - ptr[p[2]] - ptr[p[5]] + ptr[p[6]] >= cval ? 64 : 0) | // 1
  116 + (ptr[p[2]] - ptr[p[3]] - ptr[p[6]] + ptr[p[7]] >= cval ? 32 : 0) | // 2
  117 + (ptr[p[6]] - ptr[p[7]] - ptr[p[10]] + ptr[p[11]] >= cval ? 16 : 0) | // 5
  118 + (ptr[p[10]] - ptr[p[11]] - ptr[p[14]] + ptr[p[15]] >= cval ? 8 : 0) | // 8
  119 + (ptr[p[9]] - ptr[p[10]] - ptr[p[13]] + ptr[p[14]] >= cval ? 4 : 0) | // 7
  120 + (ptr[p[8]] - ptr[p[9]] - ptr[p[12]] + ptr[p[13]] >= cval ? 2 : 0) | // 6
  121 + (ptr[p[4]] - ptr[p[5]] - ptr[p[8]] + ptr[p[9]] >= cval ? 1 : 0)); // 3
  122 +}
  123 +
  124 +} // namespace br
  125 +
  126 +#include "representation/mblbp.moc"
... ...
openbr/plugins/representation/npd.cpp 0 → 100644
  1 +#include <openbr/plugins/openbr_internal.h>
  2 +
  3 +using namespace cv;
  4 +
  5 +namespace br
  6 +{
  7 +
  8 +class NPDRepresentation : public Representation
  9 +{
  10 + Q_OBJECT
  11 +
  12 + Q_PROPERTY(int winWidth READ get_winWidth WRITE set_winWidth RESET reset_winWidth STORED false)
  13 + Q_PROPERTY(int winHeight READ get_winHeight WRITE set_winHeight RESET reset_winHeight STORED false)
  14 + BR_PROPERTY(int, winWidth, 24)
  15 + BR_PROPERTY(int, winHeight, 24)
  16 +
  17 + void init()
  18 + {
  19 + for (int p1 = 0; p1 < (winWidth * winHeight); p1++)
  20 + for (int p2 = p1; p2 < (winWidth * winHeight); p2++)
  21 + features.append(Feature(p1, p2));
  22 + }
  23 +
  24 + float evaluate(const Mat &image, int idx) const
  25 + {
  26 + return features[idx].calc(image);
  27 + }
  28 +
  29 + Mat evaluate(const Mat &image, const QList<int> &indices) const
  30 + {
  31 + int size = indices.empty() ? numFeatures() : indices.size();
  32 +
  33 + Mat result(1, size, CV_32FC1);
  34 + for (int i = 0; i < size; i++)
  35 + result.at<float>(i) = evaluate(image, indices.empty() ? i : indices[i]);
  36 + return result;
  37 + }
  38 +
  39 + Size windowSize(int *dx, int *dy) const
  40 + {
  41 + if (dx && dy)
  42 + *dx = *dy = 0;
  43 + return Size(winWidth, winHeight);
  44 + }
  45 +
  46 + int numFeatures() const { return features.size(); }
  47 + int maxCatCount() const { return 0; }
  48 +
  49 + struct Feature
  50 + {
  51 + Feature() {}
  52 + Feature(int p1, int p2) { p[0] = p1; p[1] = p2; }
  53 + float calc(const Mat &image) const;
  54 +
  55 + int p[2];
  56 + };
  57 + QList<Feature> features;
  58 +};
  59 +
  60 +BR_REGISTER(Representation, NPDRepresentation)
  61 +
  62 +inline float NPDRepresentation::Feature::calc(const Mat &image) const
  63 +{
  64 + const int *ptr = image.ptr<int>();
  65 + int v1 = ptr[p[0]], v2 = ptr[p[1]];
  66 + return v1 == 0 && v2 == 0 ? 0 : ((float)(v1 - v2)) / (v1 + v2);
  67 +}
  68 +
  69 +} // namespace br
  70 +
  71 +#include "representation/npd.moc"
... ...
openbr/plugins/representation/random.cpp 0 → 100644
  1 +#include <opencv2/imgproc/imgproc.hpp>
  2 +#include <opencv2/highgui/highgui.hpp>
  3 +
  4 +#include <openbr/plugins/openbr_internal.h>
  5 +#include <openbr/core/opencvutils.h>
  6 +#include <openbr/core/common.h>
  7 +
  8 +using namespace cv;
  9 +
  10 +namespace br
  11 +{
  12 +
  13 +/*!
  14 + * \ingroup galleries
  15 + * \brief Computes first order gradient histogram features using an integral image
  16 + * \author Scott Klum \cite sklum
  17 + */
  18 +class RandomRepresentation : public Representation
  19 +{
  20 + Q_OBJECT
  21 +
  22 + Q_PROPERTY(br::Representation* representation READ get_representation WRITE set_representation RESET reset_representation STORED false)
  23 + Q_PROPERTY(int count READ get_count WRITE set_count RESET reset_count STORED false)
  24 + BR_PROPERTY(br::Representation*, representation, NULL)
  25 + BR_PROPERTY(int, count, 20000)
  26 +
  27 + QList<int> features;
  28 +
  29 + void train(const QList<Mat> &images, const QList<float> &labels)
  30 + {
  31 + representation->train(images, labels);
  32 +
  33 + const int nFeatures = representation->numFeatures();
  34 +
  35 + if (Globals->verbose)
  36 + qDebug() << "Randomly sampling from" << nFeatures << "features.";
  37 +
  38 + features = Common::RandSample(count,nFeatures,0,true);
  39 + }
  40 +
  41 + void preprocess(const Mat &src, Mat &dst) const
  42 + {
  43 + representation->preprocess(src,dst);
  44 + }
  45 +
  46 + float evaluate(const Mat &image, int idx) const
  47 + {
  48 + return representation->evaluate(image,features[idx]);
  49 + }
  50 +
  51 + Mat evaluate(const Mat &image, const QList<int> &indices) const
  52 + {
  53 + QList<int> newIndices;
  54 + if (indices.empty())
  55 + newIndices = features;
  56 + else
  57 + for (int i = 0; i < indices.size(); i++)
  58 + newIndices.append(features[indices[i]]);
  59 +
  60 + return representation->evaluate(image,newIndices);
  61 + }
  62 +
  63 + int numFeatures() const
  64 + {
  65 + return features.size();
  66 + }
  67 +
  68 + int numChannels() const
  69 + {
  70 + return representation->numChannels();
  71 + }
  72 +
  73 + Size windowSize(int *dx, int *dy) const
  74 + {
  75 + return representation->windowSize(dx,dy);
  76 + }
  77 +
  78 + int maxCatCount() const
  79 + {
  80 + return representation->maxCatCount();
  81 + }
  82 +
  83 + void load(QDataStream &stream)
  84 + {
  85 + representation->load(stream);
  86 +
  87 + int numFeatures; stream >> numFeatures;
  88 + for (int i=0; i<numFeatures; i++) {
  89 + int feature; stream >> feature;
  90 + features.append(feature);
  91 + }
  92 + }
  93 +
  94 + void store(QDataStream &stream) const
  95 + {
  96 + representation->store(stream);
  97 +
  98 + stream << features.size();
  99 + for (int i=0; i<features.size(); i++)
  100 + stream << features[i];
  101 + }
  102 +};
  103 +
  104 +BR_REGISTER(Representation, RandomRepresentation)
  105 +
  106 +} // namespace br
  107 +
  108 +#include "representation/random.moc"
  109 +
  110 +
  111 +
... ...