diff --git a/test/source.c b/test/source.c index f0094b3..644923c 100644 --- a/test/source.c +++ b/test/source.c @@ -58,1516 +58,3 @@ static inline int _ccv_run_bbf_feature(ccv_bbf_feature_t *feature, int *step, un #undef nf_at return 1; } - -static int _ccv_read_bbf_stage_classifier(const char *file, ccv_bbf_stage_classifier_t *classifier) -{ - FILE *r = fopen(file, "r"); - if (r == 0) - return -1; - int stat = 0; - stat |= fscanf(r, "%d", &classifier->count); - union { - float fl; - int i; - } fli; - stat |= fscanf(r, "%d", &fli.i); - classifier->threshold = fli.fl; - classifier->feature = (ccv_bbf_feature_t *)ccmalloc(classifier->count * sizeof(ccv_bbf_feature_t)); - classifier->alpha = (float *)ccmalloc(classifier->count * 2 * sizeof(float)); - int i, j; - for (i = 0; i < classifier->count; i++) - { - stat |= fscanf(r, "%d", &classifier->feature[i].size); - for (j = 0; j < classifier->feature[i].size; j++) - { - stat |= fscanf(r, "%d %d %d", &classifier->feature[i].px[j], &classifier->feature[i].py[j], &classifier->feature[i].pz[j]); - stat |= fscanf(r, "%d %d %d", &classifier->feature[i].nx[j], &classifier->feature[i].ny[j], &classifier->feature[i].nz[j]); - } - union { - float fl; - int i; - } flia, flib; - stat |= fscanf(r, "%d %d", &flia.i, &flib.i); - classifier->alpha[i * 2] = flia.fl; - classifier->alpha[i * 2 + 1] = flib.fl; - } - fclose(r); - return 0; -} - -#ifdef HAVE_GSL - -static unsigned int _ccv_bbf_time_measure() -{ - struct timeval tv; - gettimeofday(&tv, 0); - return tv.tv_sec * 1000000 + tv.tv_usec; -} - -#define less_than(a, b, aux) ((a) < (b)) -CCV_IMPLEMENT_QSORT(_ccv_sort_32f, float, less_than) -#undef less_than - -static void _ccv_bbf_eval_data(ccv_bbf_stage_classifier_t *classifier, unsigned char **posdata, int posnum, unsigned char **negdata, int negnum, ccv_size_t size, float *peval, float *neval) -{ - int i, j; - int steps[] = {_ccv_width_padding(size.width), - _ccv_width_padding(size.width >> 1), - _ccv_width_padding(size.width >> 2)}; - int isizs0 = steps[0] * size.height; - int isizs01 = isizs0 + steps[1] * (size.height >> 1); - for (i = 0; i < posnum; i++) - { - unsigned char *u8[] = {posdata[i], posdata[i] + isizs0, posdata[i] + isizs01}; - float sum = 0; - float *alpha = classifier->alpha; - ccv_bbf_feature_t *feature = classifier->feature; - for (j = 0; j < classifier->count; ++j, alpha += 2, ++feature) - sum += alpha[_ccv_run_bbf_feature(feature, steps, u8)]; - peval[i] = sum; - } - for (i = 0; i < negnum; i++) - { - unsigned char *u8[] = {negdata[i], negdata[i] + isizs0, negdata[i] + isizs01}; - float sum = 0; - float *alpha = classifier->alpha; - ccv_bbf_feature_t *feature = classifier->feature; - for (j = 0; j < classifier->count; ++j, alpha += 2, ++feature) - sum += alpha[_ccv_run_bbf_feature(feature, steps, u8)]; - neval[i] = sum; - } -} - -static int _ccv_prune_positive_data(ccv_bbf_classifier_cascade_t *cascade, unsigned char **posdata, int posnum, ccv_size_t size) -{ - float *peval = (float *)ccmalloc(posnum * sizeof(float)); - int i, j, k, rpos = posnum; - for (i = 0; i < cascade->count; i++) - { - _ccv_bbf_eval_data(cascade->stage_classifier + i, posdata, rpos, 0, 0, size, peval, 0); - k = 0; - for (j = 0; j < rpos; j++) - if (peval[j] >= cascade->stage_classifier[i].threshold) - { - posdata[k] = posdata[j]; - ++k; - } - else - { - ccfree(posdata[j]); - } - rpos = k; - } - ccfree(peval); - return rpos; -} - -static int _ccv_prepare_background_data(ccv_bbf_classifier_cascade_t *cascade, char **bgfiles, int bgnum, unsigned char **negdata, int negnum) -{ - int t, i, j, k, q; - int negperbg; - int negtotal = 0; - int steps[] = {_ccv_width_padding(cascade->size.width), - _ccv_width_padding(cascade->size.width >> 1), - _ccv_width_padding(cascade->size.width >> 2)}; - int isizs0 = steps[0] * cascade->size.height; - int isizs1 = steps[1] * (cascade->size.height >> 1); - int isizs2 = steps[2] * (cascade->size.height >> 2); - int *idcheck = (int *)ccmalloc(negnum * sizeof(int)); - - gsl_rng_env_setup(); - - gsl_rng *rng = gsl_rng_alloc(gsl_rng_default); - gsl_rng_set(rng, (unsigned long int)idcheck); - - ccv_size_t imgsz = cascade->size; - int rneg = negtotal; - for (t = 0; negtotal < negnum; t++) - { - PRINT(CCV_CLI_INFO, "preparing negative data ... 0%%"); - for (i = 0; i < bgnum; i++) - { - negperbg = (t < 2) ? (negnum - negtotal) / (bgnum - i) + 1 : negnum - negtotal; - ccv_dense_matrix_t *image = 0; - ccv_read(bgfiles[i], &image, CCV_IO_GRAY | CCV_IO_ANY_FILE); - assert((image->type & CCV_C1) && (image->type & CCV_8U)); - if (image == 0) - { - PRINT(CCV_CLI_ERROR, "\n%s file corrupted\n", bgfiles[i]); - continue; - } - if (t % 2 != 0) - ccv_flip(image, 0, 0, CCV_FLIP_X); - if (t % 4 >= 2) - ccv_flip(image, 0, 0, CCV_FLIP_Y); - ccv_bbf_param_t params = {.interval = 3, .min_neighbors = 0, .accurate = 1, .flags = 0, .size = cascade->size}; - ccv_array_t *detected = ccv_bbf_detect_objects(image, &cascade, 1, params); - memset(idcheck, 0, ccv_min(detected->rnum, negperbg) * sizeof(int)); - for (j = 0; j < ccv_min(detected->rnum, negperbg); j++) - { - int r = gsl_rng_uniform_int(rng, detected->rnum); - int flag = 1; - ccv_rect_t *rect = (ccv_rect_t *)ccv_array_get(detected, r); - while (flag) - { - flag = 0; - for (k = 0; k < j; k++) - if (r == idcheck[k]) - { - flag = 1; - r = gsl_rng_uniform_int(rng, detected->rnum); - break; - } - rect = (ccv_rect_t *)ccv_array_get(detected, r); - if ((rect->x < 0) || (rect->y < 0) || (rect->width + rect->x > image->cols) || (rect->height + rect->y > image->rows)) - { - flag = 1; - r = gsl_rng_uniform_int(rng, detected->rnum); - } - } - idcheck[j] = r; - ccv_dense_matrix_t *temp = 0; - ccv_dense_matrix_t *imgs0 = 0; - ccv_dense_matrix_t *imgs1 = 0; - ccv_dense_matrix_t *imgs2 = 0; - ccv_slice(image, (ccv_matrix_t **)&temp, 0, rect->y, rect->x, rect->height, rect->width); - ccv_resample(temp, &imgs0, 0, imgsz.height, imgsz.width, CCV_INTER_AREA); - assert(imgs0->step == steps[0]); - ccv_matrix_free(temp); - ccv_sample_down(imgs0, &imgs1, 0, 0, 0); - assert(imgs1->step == steps[1]); - ccv_sample_down(imgs1, &imgs2, 0, 0, 0); - assert(imgs2->step == steps[2]); - - negdata[negtotal] = (unsigned char *)ccmalloc(isizs0 + isizs1 + isizs2); - unsigned char *u8s0 = negdata[negtotal]; - unsigned char *u8s1 = negdata[negtotal] + isizs0; - unsigned char *u8s2 = negdata[negtotal] + isizs0 + isizs1; - unsigned char *u8[] = {u8s0, u8s1, u8s2}; - memcpy(u8s0, imgs0->data.u8, imgs0->rows * imgs0->step); - ccv_matrix_free(imgs0); - memcpy(u8s1, imgs1->data.u8, imgs1->rows * imgs1->step); - ccv_matrix_free(imgs1); - memcpy(u8s2, imgs2->data.u8, imgs2->rows * imgs2->step); - ccv_matrix_free(imgs2); - - flag = 1; - ccv_bbf_stage_classifier_t *classifier = cascade->stage_classifier; - for (k = 0; k < cascade->count; ++k, ++classifier) - { - float sum = 0; - float *alpha = classifier->alpha; - ccv_bbf_feature_t *feature = classifier->feature; - for (q = 0; q < classifier->count; ++q, alpha += 2, ++feature) - sum += alpha[_ccv_run_bbf_feature(feature, steps, u8)]; - if (sum < classifier->threshold) - { - flag = 0; - break; - } - } - if (!flag) - ccfree(negdata[negtotal]); - else - { - ++negtotal; - if (negtotal >= negnum) - break; - } - } - ccv_array_free(detected); - ccv_matrix_free(image); - ccv_drain_cache(); - PRINT(CCV_CLI_INFO, "\rpreparing negative data ... %2d%%", 100 * negtotal / negnum); - fflush(0); - if (negtotal >= negnum) - break; - } - if (rneg == negtotal) - break; - rneg = negtotal; - PRINT(CCV_CLI_INFO, "\nentering additional round %d\n", t + 1); - } - gsl_rng_free(rng); - ccfree(idcheck); - ccv_drain_cache(); - PRINT(CCV_CLI_INFO, "\n"); - return negtotal; -} - -static void _ccv_prepare_positive_data(ccv_dense_matrix_t **posimg, unsigned char **posdata, ccv_size_t size, int posnum) -{ - PRINT(CCV_CLI_INFO, "preparing positive data ... 0%%"); - int i; - for (i = 0; i < posnum; i++) - { - ccv_dense_matrix_t *imgs0 = posimg[i]; - ccv_dense_matrix_t *imgs1 = 0; - ccv_dense_matrix_t *imgs2 = 0; - assert((imgs0->type & CCV_C1) && (imgs0->type & CCV_8U) && imgs0->rows == size.height && imgs0->cols == size.width); - ccv_sample_down(imgs0, &imgs1, 0, 0, 0); - ccv_sample_down(imgs1, &imgs2, 0, 0, 0); - int isizs0 = imgs0->rows * imgs0->step; - int isizs1 = imgs1->rows * imgs1->step; - int isizs2 = imgs2->rows * imgs2->step; - - posdata[i] = (unsigned char *)ccmalloc(isizs0 + isizs1 + isizs2); - memcpy(posdata[i], imgs0->data.u8, isizs0); - memcpy(posdata[i] + isizs0, imgs1->data.u8, isizs1); - memcpy(posdata[i] + isizs0 + isizs1, imgs2->data.u8, isizs2); - - PRINT(CCV_CLI_INFO, "\rpreparing positive data ... %2d%%", 100 * (i + 1) / posnum); - fflush(0); - - ccv_matrix_free(imgs1); - ccv_matrix_free(imgs2); - } - ccv_drain_cache(); - PRINT(CCV_CLI_INFO, "\n"); -} - -typedef struct -{ - double fitness; - int pk, nk; - int age; - double error; - ccv_bbf_feature_t feature; -} ccv_bbf_gene_t; - -static inline void _ccv_bbf_genetic_fitness(ccv_bbf_gene_t *gene) -{ - gene->fitness = (1 - gene->error) * exp(-0.01 * gene->age) * exp((gene->pk + gene->nk) * log(1.015)); -} - -static inline int _ccv_bbf_exist_gene_feature(ccv_bbf_gene_t *gene, int x, int y, int z) -{ - int i; - for (i = 0; i < gene->pk; i++) - if (z == gene->feature.pz[i] && x == gene->feature.px[i] && y == gene->feature.py[i]) - return 1; - for (i = 0; i < gene->nk; i++) - if (z == gene->feature.nz[i] && x == gene->feature.nx[i] && y == gene->feature.ny[i]) - return 1; - return 0; -} - -static inline void _ccv_bbf_randomize_gene(gsl_rng *rng, ccv_bbf_gene_t *gene, int *rows, int *cols) -{ - int i; - do - { - gene->pk = gsl_rng_uniform_int(rng, CCV_BBF_POINT_MAX - 1) + 1; - gene->nk = gsl_rng_uniform_int(rng, CCV_BBF_POINT_MAX - 1) + 1; - } while (gene->pk + gene->nk < CCV_BBF_POINT_MIN); /* a hard restriction of at least 3 points have to be examed */ - gene->feature.size = ccv_max(gene->pk, gene->nk); - gene->age = 0; - for (i = 0; i < CCV_BBF_POINT_MAX; i++) - { - gene->feature.pz[i] = -1; - gene->feature.nz[i] = -1; - } - int x, y, z; - for (i = 0; i < gene->pk; i++) - { - do - { - z = gsl_rng_uniform_int(rng, 3); - x = gsl_rng_uniform_int(rng, cols[z]); - y = gsl_rng_uniform_int(rng, rows[z]); - } while (_ccv_bbf_exist_gene_feature(gene, x, y, z)); - gene->feature.pz[i] = z; - gene->feature.px[i] = x; - gene->feature.py[i] = y; - } - for (i = 0; i < gene->nk; i++) - { - do - { - z = gsl_rng_uniform_int(rng, 3); - x = gsl_rng_uniform_int(rng, cols[z]); - y = gsl_rng_uniform_int(rng, rows[z]); - } while (_ccv_bbf_exist_gene_feature(gene, x, y, z)); - gene->feature.nz[i] = z; - gene->feature.nx[i] = x; - gene->feature.ny[i] = y; - } -} - -static inline double _ccv_bbf_error_rate(ccv_bbf_feature_t *feature, unsigned char **posdata, int posnum, unsigned char **negdata, int negnum, ccv_size_t size, double *pw, double *nw) -{ - int i; - int steps[] = {_ccv_width_padding(size.width), - _ccv_width_padding(size.width >> 1), - _ccv_width_padding(size.width >> 2)}; - int isizs0 = steps[0] * size.height; - int isizs01 = isizs0 + steps[1] * (size.height >> 1); - double error = 0; - for (i = 0; i < posnum; i++) - { - unsigned char *u8[] = {posdata[i], posdata[i] + isizs0, posdata[i] + isizs01}; - if (!_ccv_run_bbf_feature(feature, steps, u8)) - error += pw[i]; - } - for (i = 0; i < negnum; i++) - { - unsigned char *u8[] = {negdata[i], negdata[i] + isizs0, negdata[i] + isizs01}; - if (_ccv_run_bbf_feature(feature, steps, u8)) - error += nw[i]; - } - return error; -} - -#define less_than(fit1, fit2, aux) ((fit1).fitness >= (fit2).fitness) -static CCV_IMPLEMENT_QSORT(_ccv_bbf_genetic_qsort, ccv_bbf_gene_t, less_than) -#undef less_than - - static ccv_bbf_feature_t _ccv_bbf_genetic_optimize(unsigned char **posdata, int posnum, unsigned char **negdata, int negnum, int ftnum, ccv_size_t size, double *pw, double *nw) -{ - ccv_bbf_feature_t best; - /* seed (random method) */ - gsl_rng_env_setup(); - gsl_rng *rng = gsl_rng_alloc(gsl_rng_default); - union { - unsigned long int li; - double db; - } dbli; - dbli.db = pw[0] + nw[0]; - gsl_rng_set(rng, dbli.li); - int i, j; - int pnum = ftnum * 100; - assert(pnum > 0); - ccv_bbf_gene_t *gene = (ccv_bbf_gene_t *)ccmalloc(pnum * sizeof(ccv_bbf_gene_t)); - int rows[] = {size.height, size.height >> 1, size.height >> 2}; - int cols[] = {size.width, size.width >> 1, size.width >> 2}; - for (i = 0; i < pnum; i++) - _ccv_bbf_randomize_gene(rng, &gene[i], rows, cols); - unsigned int timer = _ccv_bbf_time_measure(); -#ifdef USE_OPENMP -#pragma omp parallel for private(i) schedule(dynamic) -#endif - for (i = 0; i < pnum; i++) - gene[i].error = _ccv_bbf_error_rate(&gene[i].feature, posdata, posnum, negdata, negnum, size, pw, nw); - timer = _ccv_bbf_time_measure() - timer; - for (i = 0; i < pnum; i++) - _ccv_bbf_genetic_fitness(&gene[i]); - double best_err = 1; - int rnum = ftnum * 39; /* number of randomize */ - int mnum = ftnum * 40; /* number of mutation */ - int hnum = ftnum * 20; /* number of hybrid */ - /* iteration stop crit : best no change in 40 iterations */ - int it = 0, t; - for (t = 0; it < 40; ++it, ++t) - { - int min_id = 0; - double min_err = gene[0].error; - for (i = 1; i < pnum; i++) - if (gene[i].error < min_err) - { - min_id = i; - min_err = gene[i].error; - } - min_err = gene[min_id].error = _ccv_bbf_error_rate(&gene[min_id].feature, posdata, posnum, negdata, negnum, size, pw, nw); - if (min_err < best_err) - { - best_err = min_err; - memcpy(&best, &gene[min_id].feature, sizeof(best)); - PRINT(CCV_CLI_INFO, "best bbf feature with error %f\n|-size: %d\n|-positive point: ", best_err, best.size); - for (i = 0; i < best.size; i++) - PRINT(CCV_CLI_INFO, "(%d %d %d), ", best.px[i], best.py[i], best.pz[i]); - PRINT(CCV_CLI_INFO, "\n|-negative point: "); - for (i = 0; i < best.size; i++) - PRINT(CCV_CLI_INFO, "(%d %d %d), ", best.nx[i], best.ny[i], best.nz[i]); - PRINT(CCV_CLI_INFO, "\n"); - it = 0; - } - PRINT(CCV_CLI_INFO, "minimum error achieved in round %d(%d) : %f with %d ms\n", t, it, min_err, timer / 1000); - _ccv_bbf_genetic_qsort(gene, pnum, 0); - for (i = 0; i < ftnum; i++) - ++gene[i].age; - for (i = ftnum; i < ftnum + mnum; i++) - { - int parent = gsl_rng_uniform_int(rng, ftnum); - memcpy(gene + i, gene + parent, sizeof(ccv_bbf_gene_t)); - /* three mutation strategy : 1. add, 2. remove, 3. refine */ - int pnm, pn = gsl_rng_uniform_int(rng, 2); - int *pnk[] = {&gene[i].pk, &gene[i].nk}; - int *pnx[] = {gene[i].feature.px, gene[i].feature.nx}; - int *pny[] = {gene[i].feature.py, gene[i].feature.ny}; - int *pnz[] = {gene[i].feature.pz, gene[i].feature.nz}; - int x, y, z; - int victim, decay = 1; - do - { - switch (gsl_rng_uniform_int(rng, 3)) - { - case 0: /* add */ - if (gene[i].pk == CCV_BBF_POINT_MAX && gene[i].nk == CCV_BBF_POINT_MAX) - break; - while (*pnk[pn] + 1 > CCV_BBF_POINT_MAX) - pn = gsl_rng_uniform_int(rng, 2); - do - { - z = gsl_rng_uniform_int(rng, 3); - x = gsl_rng_uniform_int(rng, cols[z]); - y = gsl_rng_uniform_int(rng, rows[z]); - } while (_ccv_bbf_exist_gene_feature(&gene[i], x, y, z)); - pnz[pn][*pnk[pn]] = z; - pnx[pn][*pnk[pn]] = x; - pny[pn][*pnk[pn]] = y; - ++(*pnk[pn]); - gene[i].feature.size = ccv_max(gene[i].pk, gene[i].nk); - decay = gene[i].age = 0; - break; - case 1: /* remove */ - if (gene[i].pk + gene[i].nk <= CCV_BBF_POINT_MIN) /* at least 3 points have to be examed */ - break; - while (*pnk[pn] - 1 <= 0) // || *pnk[pn] + *pnk[!pn] - 1 < CCV_BBF_POINT_MIN) - pn = gsl_rng_uniform_int(rng, 2); - victim = gsl_rng_uniform_int(rng, *pnk[pn]); - for (j = victim; j < *pnk[pn] - 1; j++) - { - pnz[pn][j] = pnz[pn][j + 1]; - pnx[pn][j] = pnx[pn][j + 1]; - pny[pn][j] = pny[pn][j + 1]; - } - pnz[pn][*pnk[pn] - 1] = -1; - --(*pnk[pn]); - gene[i].feature.size = ccv_max(gene[i].pk, gene[i].nk); - decay = gene[i].age = 0; - break; - case 2: /* refine */ - pnm = gsl_rng_uniform_int(rng, *pnk[pn]); - do - { - z = gsl_rng_uniform_int(rng, 3); - x = gsl_rng_uniform_int(rng, cols[z]); - y = gsl_rng_uniform_int(rng, rows[z]); - } while (_ccv_bbf_exist_gene_feature(&gene[i], x, y, z)); - pnz[pn][pnm] = z; - pnx[pn][pnm] = x; - pny[pn][pnm] = y; - decay = gene[i].age = 0; - break; - } - } while (decay); - } - for (i = ftnum + mnum; i < ftnum + mnum + hnum; i++) - { - /* hybrid strategy: taking positive points from dad, negative points from mum */ - int dad, mum; - do - { - dad = gsl_rng_uniform_int(rng, ftnum); - mum = gsl_rng_uniform_int(rng, ftnum); - } while (dad == mum || gene[dad].pk + gene[mum].nk < CCV_BBF_POINT_MIN); /* at least 3 points have to be examed */ - for (j = 0; j < CCV_BBF_POINT_MAX; j++) - { - gene[i].feature.pz[j] = -1; - gene[i].feature.nz[j] = -1; - } - gene[i].pk = gene[dad].pk; - for (j = 0; j < gene[i].pk; j++) - { - gene[i].feature.pz[j] = gene[dad].feature.pz[j]; - gene[i].feature.px[j] = gene[dad].feature.px[j]; - gene[i].feature.py[j] = gene[dad].feature.py[j]; - } - gene[i].nk = gene[mum].nk; - for (j = 0; j < gene[i].nk; j++) - { - gene[i].feature.nz[j] = gene[mum].feature.nz[j]; - gene[i].feature.nx[j] = gene[mum].feature.nx[j]; - gene[i].feature.ny[j] = gene[mum].feature.ny[j]; - } - gene[i].feature.size = ccv_max(gene[i].pk, gene[i].nk); - gene[i].age = 0; - } - for (i = ftnum + mnum + hnum; i < ftnum + mnum + hnum + rnum; i++) - _ccv_bbf_randomize_gene(rng, &gene[i], rows, cols); - timer = _ccv_bbf_time_measure(); -#ifdef USE_OPENMP -#pragma omp parallel for private(i) schedule(dynamic) -#endif - for (i = 0; i < pnum; i++) - gene[i].error = _ccv_bbf_error_rate(&gene[i].feature, posdata, posnum, negdata, negnum, size, pw, nw); - timer = _ccv_bbf_time_measure() - timer; - for (i = 0; i < pnum; i++) - _ccv_bbf_genetic_fitness(&gene[i]); - } - ccfree(gene); - gsl_rng_free(rng); - return best; -} - -#define less_than(fit1, fit2, aux) ((fit1).error < (fit2).error) -static CCV_IMPLEMENT_QSORT(_ccv_bbf_best_qsort, ccv_bbf_gene_t, less_than) -#undef less_than - - static ccv_bbf_gene_t _ccv_bbf_best_gene(ccv_bbf_gene_t *gene, int pnum, int point_min, unsigned char **posdata, int posnum, unsigned char **negdata, int negnum, ccv_size_t size, double *pw, double *nw) -{ - int i; - unsigned int timer = _ccv_bbf_time_measure(); -#ifdef USE_OPENMP -#pragma omp parallel for private(i) schedule(dynamic) -#endif - for (i = 0; i < pnum; i++) - gene[i].error = _ccv_bbf_error_rate(&gene[i].feature, posdata, posnum, negdata, negnum, size, pw, nw); - timer = _ccv_bbf_time_measure() - timer; - _ccv_bbf_best_qsort(gene, pnum, 0); - int min_id = 0; - double min_err = gene[0].error; - for (i = 0; i < pnum; i++) - if (gene[i].nk + gene[i].pk >= point_min) - { - min_id = i; - min_err = gene[i].error; - break; - } - PRINT(CCV_CLI_INFO, "local best bbf feature with error %f\n|-size: %d\n|-positive point: ", min_err, gene[min_id].feature.size); - for (i = 0; i < gene[min_id].feature.size; i++) - PRINT(CCV_CLI_INFO, "(%d %d %d), ", gene[min_id].feature.px[i], gene[min_id].feature.py[i], gene[min_id].feature.pz[i]); - PRINT(CCV_CLI_INFO, "\n|-negative point: "); - for (i = 0; i < gene[min_id].feature.size; i++) - PRINT(CCV_CLI_INFO, "(%d %d %d), ", gene[min_id].feature.nx[i], gene[min_id].feature.ny[i], gene[min_id].feature.nz[i]); - PRINT(CCV_CLI_INFO, "\nthe computation takes %d ms\n", timer / 1000); - return gene[min_id]; -} - -static ccv_bbf_feature_t _ccv_bbf_convex_optimize(unsigned char **posdata, int posnum, unsigned char **negdata, int negnum, ccv_bbf_feature_t *best_feature, ccv_size_t size, double *pw, double *nw) -{ - ccv_bbf_gene_t best_gene; - /* seed (random method) */ - gsl_rng_env_setup(); - gsl_rng *rng = gsl_rng_alloc(gsl_rng_default); - union { - unsigned long int li; - double db; - } dbli; - dbli.db = pw[0] + nw[0]; - gsl_rng_set(rng, dbli.li); - int i, j, k, q, p, g, t; - int rows[] = {size.height, size.height >> 1, size.height >> 2}; - int cols[] = {size.width, size.width >> 1, size.width >> 2}; - int pnum = rows[0] * cols[0] + rows[1] * cols[1] + rows[2] * cols[2]; - ccv_bbf_gene_t *gene = (ccv_bbf_gene_t *)ccmalloc((pnum * (CCV_BBF_POINT_MAX * 2 + 1) * 2 + CCV_BBF_POINT_MAX * 2 + 1) * sizeof(ccv_bbf_gene_t)); - if (best_feature == 0) - { - /* bootstrapping the best feature, start from two pixels, one for positive, one for negative - * the bootstrapping process go like this: first, it will assign a random pixel as positive - * and enumerate every possible pixel as negative, and pick the best one. Then, enumerate every - * possible pixel as positive, and pick the best one, until it converges */ - memset(&best_gene, 0, sizeof(ccv_bbf_gene_t)); - for (i = 0; i < CCV_BBF_POINT_MAX; i++) - best_gene.feature.pz[i] = best_gene.feature.nz[i] = -1; - best_gene.pk = 1; - best_gene.nk = 0; - best_gene.feature.size = 1; - best_gene.feature.pz[0] = gsl_rng_uniform_int(rng, 3); - best_gene.feature.px[0] = gsl_rng_uniform_int(rng, cols[best_gene.feature.pz[0]]); - best_gene.feature.py[0] = gsl_rng_uniform_int(rng, rows[best_gene.feature.pz[0]]); - for (t = 0;; ++t) - { - g = 0; - if (t % 2 == 0) - { - for (i = 0; i < 3; i++) - for (j = 0; j < cols[i]; j++) - for (k = 0; k < rows[i]; k++) - if (i != best_gene.feature.pz[0] || j != best_gene.feature.px[0] || k != best_gene.feature.py[0]) - { - gene[g] = best_gene; - gene[g].pk = gene[g].nk = 1; - gene[g].feature.nz[0] = i; - gene[g].feature.nx[0] = j; - gene[g].feature.ny[0] = k; - g++; - } - } - else - { - for (i = 0; i < 3; i++) - for (j = 0; j < cols[i]; j++) - for (k = 0; k < rows[i]; k++) - if (i != best_gene.feature.nz[0] || j != best_gene.feature.nx[0] || k != best_gene.feature.ny[0]) - { - gene[g] = best_gene; - gene[g].pk = gene[g].nk = 1; - gene[g].feature.pz[0] = i; - gene[g].feature.px[0] = j; - gene[g].feature.py[0] = k; - g++; - } - } - PRINT(CCV_CLI_INFO, "bootstrapping round : %d\n", t); - ccv_bbf_gene_t local_gene = _ccv_bbf_best_gene(gene, g, 2, posdata, posnum, negdata, negnum, size, pw, nw); - if (local_gene.error >= best_gene.error - 1e-10) - break; - best_gene = local_gene; - } - } - else - { - best_gene.feature = *best_feature; - best_gene.pk = best_gene.nk = best_gene.feature.size; - for (i = 0; i < CCV_BBF_POINT_MAX; i++) - if (best_feature->pz[i] == -1) - { - best_gene.pk = i; - break; - } - for (i = 0; i < CCV_BBF_POINT_MAX; i++) - if (best_feature->nz[i] == -1) - { - best_gene.nk = i; - break; - } - } - /* after bootstrapping, the float search technique will do the following permutations: - * a). add a new point to positive or negative - * b). remove a point from positive or negative - * c). move an existing point in positive or negative to another position - * the three rules applied exhaustively, no heuristic used. */ - for (t = 0;; ++t) - { - g = 0; - for (i = 0; i < 3; i++) - for (j = 0; j < cols[i]; j++) - for (k = 0; k < rows[i]; k++) - if (!_ccv_bbf_exist_gene_feature(&best_gene, j, k, i)) - { - /* add positive point */ - if (best_gene.pk < CCV_BBF_POINT_MAX - 1) - { - gene[g] = best_gene; - gene[g].feature.pz[gene[g].pk] = i; - gene[g].feature.px[gene[g].pk] = j; - gene[g].feature.py[gene[g].pk] = k; - gene[g].pk++; - gene[g].feature.size = ccv_max(gene[g].pk, gene[g].nk); - g++; - } - /* add negative point */ - if (best_gene.nk < CCV_BBF_POINT_MAX - 1) - { - gene[g] = best_gene; - gene[g].feature.nz[gene[g].nk] = i; - gene[g].feature.nx[gene[g].nk] = j; - gene[g].feature.ny[gene[g].nk] = k; - gene[g].nk++; - gene[g].feature.size = ccv_max(gene[g].pk, gene[g].nk); - g++; - } - /* refine positive point */ - for (q = 0; q < best_gene.pk; q++) - { - gene[g] = best_gene; - gene[g].feature.pz[q] = i; - gene[g].feature.px[q] = j; - gene[g].feature.py[q] = k; - g++; - } - /* add positive point, remove negative point */ - if (best_gene.pk < CCV_BBF_POINT_MAX - 1 && best_gene.nk > 1) - { - for (q = 0; q < best_gene.nk; q++) - { - gene[g] = best_gene; - gene[g].feature.pz[gene[g].pk] = i; - gene[g].feature.px[gene[g].pk] = j; - gene[g].feature.py[gene[g].pk] = k; - gene[g].pk++; - for (p = q; p < best_gene.nk - 1; p++) - { - gene[g].feature.nz[p] = gene[g].feature.nz[p + 1]; - gene[g].feature.nx[p] = gene[g].feature.nx[p + 1]; - gene[g].feature.ny[p] = gene[g].feature.ny[p + 1]; - } - gene[g].feature.nz[gene[g].nk - 1] = -1; - gene[g].nk--; - gene[g].feature.size = ccv_max(gene[g].pk, gene[g].nk); - g++; - } - } - /* refine negative point */ - for (q = 0; q < best_gene.nk; q++) - { - gene[g] = best_gene; - gene[g].feature.nz[q] = i; - gene[g].feature.nx[q] = j; - gene[g].feature.ny[q] = k; - g++; - } - /* add negative point, remove positive point */ - if (best_gene.pk > 1 && best_gene.nk < CCV_BBF_POINT_MAX - 1) - { - for (q = 0; q < best_gene.pk; q++) - { - gene[g] = best_gene; - gene[g].feature.nz[gene[g].nk] = i; - gene[g].feature.nx[gene[g].nk] = j; - gene[g].feature.ny[gene[g].nk] = k; - gene[g].nk++; - for (p = q; p < best_gene.pk - 1; p++) - { - gene[g].feature.pz[p] = gene[g].feature.pz[p + 1]; - gene[g].feature.px[p] = gene[g].feature.px[p + 1]; - gene[g].feature.py[p] = gene[g].feature.py[p + 1]; - } - gene[g].feature.pz[gene[g].pk - 1] = -1; - gene[g].pk--; - gene[g].feature.size = ccv_max(gene[g].pk, gene[g].nk); - g++; - } - } - } - if (best_gene.pk > 1) - for (q = 0; q < best_gene.pk; q++) - { - gene[g] = best_gene; - for (i = q; i < best_gene.pk - 1; i++) - { - gene[g].feature.pz[i] = gene[g].feature.pz[i + 1]; - gene[g].feature.px[i] = gene[g].feature.px[i + 1]; - gene[g].feature.py[i] = gene[g].feature.py[i + 1]; - } - gene[g].feature.pz[gene[g].pk - 1] = -1; - gene[g].pk--; - gene[g].feature.size = ccv_max(gene[g].pk, gene[g].nk); - g++; - } - if (best_gene.nk > 1) - for (q = 0; q < best_gene.nk; q++) - { - gene[g] = best_gene; - for (i = q; i < best_gene.nk - 1; i++) - { - gene[g].feature.nz[i] = gene[g].feature.nz[i + 1]; - gene[g].feature.nx[i] = gene[g].feature.nx[i + 1]; - gene[g].feature.ny[i] = gene[g].feature.ny[i + 1]; - } - gene[g].feature.nz[gene[g].nk - 1] = -1; - gene[g].nk--; - gene[g].feature.size = ccv_max(gene[g].pk, gene[g].nk); - g++; - } - gene[g] = best_gene; - g++; - PRINT(CCV_CLI_INFO, "float search round : %d\n", t); - ccv_bbf_gene_t local_gene = _ccv_bbf_best_gene(gene, g, CCV_BBF_POINT_MIN, posdata, posnum, negdata, negnum, size, pw, nw); - if (local_gene.error >= best_gene.error - 1e-10) - break; - best_gene = local_gene; - } - ccfree(gene); - gsl_rng_free(rng); - return best_gene.feature; -} - -static int _ccv_write_bbf_stage_classifier(const char *file, ccv_bbf_stage_classifier_t *classifier) -{ - FILE *w = fopen(file, "wb"); - if (w == 0) - return -1; - fprintf(w, "%d\n", classifier->count); - union { - float fl; - int i; - } fli; - fli.fl = classifier->threshold; - fprintf(w, "%d\n", fli.i); - int i, j; - for (i = 0; i < classifier->count; i++) - { - fprintf(w, "%d\n", classifier->feature[i].size); - for (j = 0; j < classifier->feature[i].size; j++) - { - fprintf(w, "%d %d %d\n", classifier->feature[i].px[j], classifier->feature[i].py[j], classifier->feature[i].pz[j]); - fprintf(w, "%d %d %d\n", classifier->feature[i].nx[j], classifier->feature[i].ny[j], classifier->feature[i].nz[j]); - } - union { - float fl; - int i; - } flia, flib; - flia.fl = classifier->alpha[i * 2]; - flib.fl = classifier->alpha[i * 2 + 1]; - fprintf(w, "%d %d\n", flia.i, flib.i); - } - fclose(w); - return 0; -} - -static int _ccv_read_background_data(const char *file, unsigned char **negdata, int *negnum, ccv_size_t size) -{ - int stat = 0; - FILE *r = fopen(file, "rb"); - if (r == 0) - return -1; - stat |= fread(negnum, sizeof(int), 1, r); - int i; - int isizs012 = _ccv_width_padding(size.width) * size.height + - _ccv_width_padding(size.width >> 1) * (size.height >> 1) + - _ccv_width_padding(size.width >> 2) * (size.height >> 2); - for (i = 0; i < *negnum; i++) - { - negdata[i] = (unsigned char *)ccmalloc(isizs012); - stat |= fread(negdata[i], 1, isizs012, r); - } - fclose(r); - return 0; -} - -static int _ccv_write_background_data(const char *file, unsigned char **negdata, int negnum, ccv_size_t size) -{ - FILE *w = fopen(file, "w"); - if (w == 0) - return -1; - fwrite(&negnum, sizeof(int), 1, w); - int i; - int isizs012 = _ccv_width_padding(size.width) * size.height + - _ccv_width_padding(size.width >> 1) * (size.height >> 1) + - _ccv_width_padding(size.width >> 2) * (size.height >> 2); - for (i = 0; i < negnum; i++) - fwrite(negdata[i], 1, isizs012, w); - fclose(w); - return 0; -} - -static int _ccv_resume_bbf_cascade_training_state(const char *file, int *i, int *k, int *bg, double *pw, double *nw, int posnum, int negnum) -{ - int stat = 0; - FILE *r = fopen(file, "r"); - if (r == 0) - return -1; - stat |= fscanf(r, "%d %d %d", i, k, bg); - int j; - union { - double db; - int i[2]; - } dbi; - for (j = 0; j < posnum; j++) - { - stat |= fscanf(r, "%d %d", &dbi.i[0], &dbi.i[1]); - pw[j] = dbi.db; - } - for (j = 0; j < negnum; j++) - { - stat |= fscanf(r, "%d %d", &dbi.i[0], &dbi.i[1]); - nw[j] = dbi.db; - } - fclose(r); - return 0; -} - -static int _ccv_save_bbf_cacade_training_state(const char *file, int i, int k, int bg, double *pw, double *nw, int posnum, int negnum) -{ - FILE *w = fopen(file, "w"); - if (w == 0) - return -1; - fprintf(w, "%d %d %d\n", i, k, bg); - int j; - union { - double db; - int i[2]; - } dbi; - for (j = 0; j < posnum; ++j) - { - dbi.db = pw[j]; - fprintf(w, "%d %d ", dbi.i[0], dbi.i[1]); - } - fprintf(w, "\n"); - for (j = 0; j < negnum; ++j) - { - dbi.db = nw[j]; - fprintf(w, "%d %d ", dbi.i[0], dbi.i[1]); - } - fprintf(w, "\n"); - fclose(w); - return 0; -} - -void ccv_bbf_classifier_cascade_new(ccv_dense_matrix_t **posimg, int posnum, char **bgfiles, int bgnum, int negnum, ccv_size_t size, const char *dir, ccv_bbf_new_param_t params) -{ - int i, j, k; - /* allocate memory for usage */ - ccv_bbf_classifier_cascade_t *cascade = (ccv_bbf_classifier_cascade_t *)ccmalloc(sizeof(ccv_bbf_classifier_cascade_t)); - cascade->count = 0; - cascade->size = size; - cascade->stage_classifier = (ccv_bbf_stage_classifier_t *)ccmalloc(sizeof(ccv_bbf_stage_classifier_t)); - unsigned char **posdata = (unsigned char **)ccmalloc(posnum * sizeof(unsigned char *)); - unsigned char **negdata = (unsigned char **)ccmalloc(negnum * sizeof(unsigned char *)); - double *pw = (double *)ccmalloc(posnum * sizeof(double)); - double *nw = (double *)ccmalloc(negnum * sizeof(double)); - float *peval = (float *)ccmalloc(posnum * sizeof(float)); - float *neval = (float *)ccmalloc(negnum * sizeof(float)); - double inv_balance_k = 1. / params.balance_k; - /* balance factor k, and weighted with 0.01 */ - params.balance_k *= 0.01; - inv_balance_k *= 0.01; - - int steps[] = {_ccv_width_padding(cascade->size.width), - _ccv_width_padding(cascade->size.width >> 1), - _ccv_width_padding(cascade->size.width >> 2)}; - int isizs0 = steps[0] * cascade->size.height; - int isizs01 = isizs0 + steps[1] * (cascade->size.height >> 1); - - i = 0; - k = 0; - int bg = 0; - int cacheK = 10; - /* state resume code */ - char buf[1024]; - sprintf(buf, "%s/stat.txt", dir); - _ccv_resume_bbf_cascade_training_state(buf, &i, &k, &bg, pw, nw, posnum, negnum); - if (i > 0) - { - cascade->count = i; - ccfree(cascade->stage_classifier); - cascade->stage_classifier = (ccv_bbf_stage_classifier_t *)ccmalloc(i * sizeof(ccv_bbf_stage_classifier_t)); - for (j = 0; j < i; j++) - { - sprintf(buf, "%s/stage-%d.txt", dir, j); - _ccv_read_bbf_stage_classifier(buf, &cascade->stage_classifier[j]); - } - } - if (k > 0) - cacheK = k; - int rpos, rneg = 0; - if (bg) - { - sprintf(buf, "%s/negs.txt", dir); - _ccv_read_background_data(buf, negdata, &rneg, cascade->size); - } - - for (; i < params.layer; i++) - { - if (!bg) - { - rneg = _ccv_prepare_background_data(cascade, bgfiles, bgnum, negdata, negnum); - /* save state of background data */ - sprintf(buf, "%s/negs.txt", dir); - _ccv_write_background_data(buf, negdata, rneg, cascade->size); - bg = 1; - } - double totalw; - /* save state of cascade : level, weight etc. */ - sprintf(buf, "%s/stat.txt", dir); - _ccv_save_bbf_cacade_training_state(buf, i, k, bg, pw, nw, posnum, negnum); - ccv_bbf_stage_classifier_t classifier; - if (k > 0) - { - /* resume state of classifier */ - sprintf(buf, "%s/stage-%d.txt", dir, i); - _ccv_read_bbf_stage_classifier(buf, &classifier); - } - else - { - /* initialize classifier */ - for (j = 0; j < posnum; j++) - pw[j] = params.balance_k; - for (j = 0; j < rneg; j++) - nw[j] = inv_balance_k; - classifier.count = k; - classifier.threshold = 0; - classifier.feature = (ccv_bbf_feature_t *)ccmalloc(cacheK * sizeof(ccv_bbf_feature_t)); - classifier.alpha = (float *)ccmalloc(cacheK * 2 * sizeof(float)); - } - _ccv_prepare_positive_data(posimg, posdata, cascade->size, posnum); - rpos = _ccv_prune_positive_data(cascade, posdata, posnum, cascade->size); - PRINT(CCV_CLI_INFO, "%d postivie data and %d negative data in training\n", rpos, rneg); - /* reweight to 1.00 */ - totalw = 0; - for (j = 0; j < rpos; j++) - totalw += pw[j]; - for (j = 0; j < rneg; j++) - totalw += nw[j]; - for (j = 0; j < rpos; j++) - pw[j] = pw[j] / totalw; - for (j = 0; j < rneg; j++) - nw[j] = nw[j] / totalw; - for (;; k++) - { - /* get overall true-positive, false-positive rate and threshold */ - double tp = 0, fp = 0, etp = 0, efp = 0; - _ccv_bbf_eval_data(&classifier, posdata, rpos, negdata, rneg, cascade->size, peval, neval); - _ccv_sort_32f(peval, rpos, 0); - classifier.threshold = peval[(int)((1. - params.pos_crit) * rpos)] - 1e-6; - for (j = 0; j < rpos; j++) - { - if (peval[j] >= 0) - ++tp; - if (peval[j] >= classifier.threshold) - ++etp; - } - tp /= rpos; - etp /= rpos; - for (j = 0; j < rneg; j++) - { - if (neval[j] >= 0) - ++fp; - if (neval[j] >= classifier.threshold) - ++efp; - } - fp /= rneg; - efp /= rneg; - PRINT(CCV_CLI_INFO, "stage classifier real TP rate : %f, FP rate : %f\n", tp, fp); - PRINT(CCV_CLI_INFO, "stage classifier TP rate : %f, FP rate : %f at threshold : %f\n", etp, efp, classifier.threshold); - if (k > 0) - { - /* save classifier state */ - sprintf(buf, "%s/stage-%d.txt", dir, i); - _ccv_write_bbf_stage_classifier(buf, &classifier); - sprintf(buf, "%s/stat.txt", dir); - _ccv_save_bbf_cacade_training_state(buf, i, k, bg, pw, nw, posnum, negnum); - } - if (etp > params.pos_crit && efp < params.neg_crit) - break; - /* TODO: more post-process is needed in here */ - - /* select the best feature in current distribution through genetic algorithm optimization */ - ccv_bbf_feature_t best; - if (params.optimizer == CCV_BBF_GENETIC_OPT) - { - best = _ccv_bbf_genetic_optimize(posdata, rpos, negdata, rneg, params.feature_number, cascade->size, pw, nw); - } - else if (params.optimizer == CCV_BBF_FLOAT_OPT) - { - best = _ccv_bbf_convex_optimize(posdata, rpos, negdata, rneg, 0, cascade->size, pw, nw); - } - else - { - best = _ccv_bbf_genetic_optimize(posdata, rpos, negdata, rneg, params.feature_number, cascade->size, pw, nw); - best = _ccv_bbf_convex_optimize(posdata, rpos, negdata, rneg, &best, cascade->size, pw, nw); - } - double err = _ccv_bbf_error_rate(&best, posdata, rpos, negdata, rneg, cascade->size, pw, nw); - double rw = (1 - err) / err; - totalw = 0; - /* reweight */ - for (j = 0; j < rpos; j++) - { - unsigned char *u8[] = {posdata[j], posdata[j] + isizs0, posdata[j] + isizs01}; - if (!_ccv_run_bbf_feature(&best, steps, u8)) - pw[j] *= rw; - pw[j] *= params.balance_k; - totalw += pw[j]; - } - for (j = 0; j < rneg; j++) - { - unsigned char *u8[] = {negdata[j], negdata[j] + isizs0, negdata[j] + isizs01}; - if (_ccv_run_bbf_feature(&best, steps, u8)) - nw[j] *= rw; - nw[j] *= inv_balance_k; - totalw += nw[j]; - } - for (j = 0; j < rpos; j++) - pw[j] = pw[j] / totalw; - for (j = 0; j < rneg; j++) - nw[j] = nw[j] / totalw; - double c = log(rw); - PRINT(CCV_CLI_INFO, "coefficient of feature %d: %f\n", k + 1, c); - classifier.count = k + 1; - /* resizing classifier */ - if (k >= cacheK) - { - ccv_bbf_feature_t *feature = (ccv_bbf_feature_t *)ccmalloc(cacheK * 2 * sizeof(ccv_bbf_feature_t)); - memcpy(feature, classifier.feature, cacheK * sizeof(ccv_bbf_feature_t)); - ccfree(classifier.feature); - float *alpha = (float *)ccmalloc(cacheK * 4 * sizeof(float)); - memcpy(alpha, classifier.alpha, cacheK * 2 * sizeof(float)); - ccfree(classifier.alpha); - classifier.feature = feature; - classifier.alpha = alpha; - cacheK *= 2; - } - /* setup new feature */ - classifier.feature[k] = best; - classifier.alpha[k * 2] = -c; - classifier.alpha[k * 2 + 1] = c; - } - cascade->count = i + 1; - ccv_bbf_stage_classifier_t *stage_classifier = (ccv_bbf_stage_classifier_t *)ccmalloc(cascade->count * sizeof(ccv_bbf_stage_classifier_t)); - memcpy(stage_classifier, cascade->stage_classifier, i * sizeof(ccv_bbf_stage_classifier_t)); - ccfree(cascade->stage_classifier); - stage_classifier[i] = classifier; - cascade->stage_classifier = stage_classifier; - k = 0; - bg = 0; - for (j = 0; j < rpos; j++) - ccfree(posdata[j]); - for (j = 0; j < rneg; j++) - ccfree(negdata[j]); - } - - ccfree(neval); - ccfree(peval); - ccfree(nw); - ccfree(pw); - ccfree(negdata); - ccfree(posdata); - ccfree(cascade); -} -#else -void ccv_bbf_classifier_cascade_new(ccv_dense_matrix_t **posimg, int posnum, char **bgfiles, int bgnum, int negnum, ccv_size_t size, const char *dir, ccv_bbf_new_param_t params) -{ - fprintf(stderr, " ccv_bbf_classifier_cascade_new requires libgsl support, please compile ccv with libgsl.\n"); -} -#endif - -static int _ccv_is_equal(const void *_r1, const void *_r2, void *data) -{ - const ccv_comp_t *r1 = (const ccv_comp_t *)_r1; - const ccv_comp_t *r2 = (const ccv_comp_t *)_r2; - int distance = (int)(r1->rect.width * 0.25 + 0.5); - - return r2->rect.x <= r1->rect.x + distance && - r2->rect.x >= r1->rect.x - distance && - r2->rect.y <= r1->rect.y + distance && - r2->rect.y >= r1->rect.y - distance && - r2->rect.width <= (int)(r1->rect.width * 1.5 + 0.5) && - (int)(r2->rect.width * 1.5 + 0.5) >= r1->rect.width; -} - -static int _ccv_is_equal_same_class(const void *_r1, const void *_r2, void *data) -{ - const ccv_comp_t *r1 = (const ccv_comp_t *)_r1; - const ccv_comp_t *r2 = (const ccv_comp_t *)_r2; - int distance = (int)(r1->rect.width * 0.25 + 0.5); - - return r2->classification.id == r1->classification.id && - r2->rect.x <= r1->rect.x + distance && - r2->rect.x >= r1->rect.x - distance && - r2->rect.y <= r1->rect.y + distance && - r2->rect.y >= r1->rect.y - distance && - r2->rect.width <= (int)(r1->rect.width * 1.5 + 0.5) && - (int)(r2->rect.width * 1.5 + 0.5) >= r1->rect.width; -} - -ccv_array_t *ccv_bbf_detect_objects(ccv_dense_matrix_t *a, ccv_bbf_classifier_cascade_t **_cascade, int count, ccv_bbf_param_t params) -{ - int hr = a->rows / params.size.height; - int wr = a->cols / params.size.width; - double scale = pow(2., 1. / (params.interval + 1.)); - int next = params.interval + 1; - int scale_upto = (int)(log((double)ccv_min(hr, wr)) / log(scale)); - ccv_dense_matrix_t **pyr = (ccv_dense_matrix_t **)alloca((scale_upto + next * 2) * 4 * sizeof(ccv_dense_matrix_t *)); - memset(pyr, 0, (scale_upto + next * 2) * 4 * sizeof(ccv_dense_matrix_t *)); - if (params.size.height != _cascade[0]->size.height || params.size.width != _cascade[0]->size.width) - ccv_resample(a, &pyr[0], 0, a->rows * _cascade[0]->size.height / params.size.height, a->cols * _cascade[0]->size.width / params.size.width, CCV_INTER_AREA); - else - pyr[0] = a; - int i, j, k, t, x, y, q; - for (i = 1; i < ccv_min(params.interval + 1, scale_upto + next * 2); i++) - ccv_resample(pyr[0], &pyr[i * 4], 0, (int)(pyr[0]->rows / pow(scale, i)), (int)(pyr[0]->cols / pow(scale, i)), CCV_INTER_AREA); - for (i = next; i < scale_upto + next * 2; i++) - ccv_sample_down(pyr[i * 4 - next * 4], &pyr[i * 4], 0, 0, 0); - if (params.accurate) - for (i = next * 2; i < scale_upto + next * 2; i++) - { - ccv_sample_down(pyr[i * 4 - next * 4], &pyr[i * 4 + 1], 0, 1, 0); - ccv_sample_down(pyr[i * 4 - next * 4], &pyr[i * 4 + 2], 0, 0, 1); - ccv_sample_down(pyr[i * 4 - next * 4], &pyr[i * 4 + 3], 0, 1, 1); - } - ccv_array_t *idx_seq; - ccv_array_t *seq = ccv_array_new(sizeof(ccv_comp_t), 64, 0); - ccv_array_t *seq2 = ccv_array_new(sizeof(ccv_comp_t), 64, 0); - ccv_array_t *result_seq = ccv_array_new(sizeof(ccv_comp_t), 64, 0); - /* detect in multi scale */ - for (t = 0; t < count; t++) - { - ccv_bbf_classifier_cascade_t *cascade = _cascade[t]; - float scale_x = (float)params.size.width / (float)cascade->size.width; - float scale_y = (float)params.size.height / (float)cascade->size.height; - ccv_array_clear(seq); - for (i = 0; i < scale_upto; i++) - { - int dx[] = {0, 1, 0, 1}; - int dy[] = {0, 0, 1, 1}; - int i_rows = pyr[i * 4 + next * 8]->rows - (cascade->size.height >> 2); - int steps[] = {pyr[i * 4]->step, pyr[i * 4 + next * 4]->step, pyr[i * 4 + next * 8]->step}; - int i_cols = pyr[i * 4 + next * 8]->cols - (cascade->size.width >> 2); - int paddings[] = {pyr[i * 4]->step * 4 - i_cols * 4, - pyr[i * 4 + next * 4]->step * 2 - i_cols * 2, - pyr[i * 4 + next * 8]->step - i_cols}; - for (q = 0; q < (params.accurate ? 4 : 1); q++) - { - unsigned char *u8[] = {pyr[i * 4]->data.u8 + dx[q] * 2 + dy[q] * pyr[i * 4]->step * 2, pyr[i * 4 + next * 4]->data.u8 + dx[q] + dy[q] * pyr[i * 4 + next * 4]->step, pyr[i * 4 + next * 8 + q]->data.u8}; - for (y = 0; y < i_rows; y++) - { - for (x = 0; x < i_cols; x++) - { - float sum; - int flag = 1; - ccv_bbf_stage_classifier_t *classifier = cascade->stage_classifier; - for (j = 0; j < cascade->count; ++j, ++classifier) - { - sum = 0; - float *alpha = classifier->alpha; - ccv_bbf_feature_t *feature = classifier->feature; - for (k = 0; k < classifier->count; ++k, alpha += 2, ++feature) - sum += alpha[_ccv_run_bbf_feature(feature, steps, u8)]; - if (sum < classifier->threshold) - { - flag = 0; - break; - } - } - if (flag) - { - ccv_comp_t comp; - comp.rect = ccv_rect((int)((x * 4 + dx[q] * 2) * scale_x + 0.5), (int)((y * 4 + dy[q] * 2) * scale_y + 0.5), (int)(cascade->size.width * scale_x + 0.5), (int)(cascade->size.height * scale_y + 0.5)); - comp.neighbors = 1; - comp.classification.id = t; - comp.classification.confidence = sum; - ccv_array_push(seq, &comp); - } - u8[0] += 4; - u8[1] += 2; - u8[2] += 1; - } - u8[0] += paddings[0]; - u8[1] += paddings[1]; - u8[2] += paddings[2]; - } - } - scale_x *= scale; - scale_y *= scale; - } - - /* the following code from OpenCV's haar feature implementation */ - if (params.min_neighbors == 0) - { - for (i = 0; i < seq->rnum; i++) - { - ccv_comp_t *comp = (ccv_comp_t *)ccv_array_get(seq, i); - ccv_array_push(result_seq, comp); - } - } - else - { - idx_seq = 0; - ccv_array_clear(seq2); - // group retrieved rectangles in order to filter out noise - int ncomp = ccv_array_group(seq, &idx_seq, _ccv_is_equal_same_class, 0); - ccv_comp_t *comps = (ccv_comp_t *)ccmalloc((ncomp + 1) * sizeof(ccv_comp_t)); - memset(comps, 0, (ncomp + 1) * sizeof(ccv_comp_t)); - - // count number of neighbors - for (i = 0; i < seq->rnum; i++) - { - ccv_comp_t r1 = *(ccv_comp_t *)ccv_array_get(seq, i); - int idx = *(int *)ccv_array_get(idx_seq, i); - - if (comps[idx].neighbors == 0) - comps[idx].classification.confidence = r1.classification.confidence; - - ++comps[idx].neighbors; - - comps[idx].rect.x += r1.rect.x; - comps[idx].rect.y += r1.rect.y; - comps[idx].rect.width += r1.rect.width; - comps[idx].rect.height += r1.rect.height; - comps[idx].classification.id = r1.classification.id; - comps[idx].classification.confidence = ccv_max(comps[idx].classification.confidence, r1.classification.confidence); - } - - // calculate average bounding box - for (i = 0; i < ncomp; i++) - { - int n = comps[i].neighbors; - if (n >= params.min_neighbors) - { - ccv_comp_t comp; - comp.rect.x = (comps[i].rect.x * 2 + n) / (2 * n); - comp.rect.y = (comps[i].rect.y * 2 + n) / (2 * n); - comp.rect.width = (comps[i].rect.width * 2 + n) / (2 * n); - comp.rect.height = (comps[i].rect.height * 2 + n) / (2 * n); - comp.neighbors = comps[i].neighbors; - comp.classification.id = comps[i].classification.id; - comp.classification.confidence = comps[i].classification.confidence; - ccv_array_push(seq2, &comp); - } - } - - // filter out small face rectangles inside large face rectangles - for (i = 0; i < seq2->rnum; i++) - { - ccv_comp_t r1 = *(ccv_comp_t *)ccv_array_get(seq2, i); - int flag = 1; - - for (j = 0; j < seq2->rnum; j++) - { - ccv_comp_t r2 = *(ccv_comp_t *)ccv_array_get(seq2, j); - int distance = (int)(r2.rect.width * 0.25 + 0.5); - - if (i != j && - r1.classification.id == r2.classification.id && - r1.rect.x >= r2.rect.x - distance && - r1.rect.y >= r2.rect.y - distance && - r1.rect.x + r1.rect.width <= r2.rect.x + r2.rect.width + distance && - r1.rect.y + r1.rect.height <= r2.rect.y + r2.rect.height + distance && - (r2.neighbors > ccv_max(3, r1.neighbors) || r1.neighbors < 3)) - { - flag = 0; - break; - } - } - - if (flag) - ccv_array_push(result_seq, &r1); - } - ccv_array_free(idx_seq); - ccfree(comps); - } - } - - ccv_array_free(seq); - ccv_array_free(seq2); - - ccv_array_t *result_seq2; - /* the following code from OpenCV's haar feature implementation */ - if (params.flags & CCV_BBF_NO_NESTED) - { - result_seq2 = ccv_array_new(sizeof(ccv_comp_t), 64, 0); - idx_seq = 0; - // group retrieved rectangles in order to filter out noise - int ncomp = ccv_array_group(result_seq, &idx_seq, _ccv_is_equal, 0); - ccv_comp_t *comps = (ccv_comp_t *)ccmalloc((ncomp + 1) * sizeof(ccv_comp_t)); - memset(comps, 0, (ncomp + 1) * sizeof(ccv_comp_t)); - - // count number of neighbors - for (i = 0; i < result_seq->rnum; i++) - { - ccv_comp_t r1 = *(ccv_comp_t *)ccv_array_get(result_seq, i); - int idx = *(int *)ccv_array_get(idx_seq, i); - - if (comps[idx].neighbors == 0 || comps[idx].classification.confidence < r1.classification.confidence) - { - comps[idx].classification.confidence = r1.classification.confidence; - comps[idx].neighbors = 1; - comps[idx].rect = r1.rect; - comps[idx].classification.id = r1.classification.id; - } - } - - // calculate average bounding box - for (i = 0; i < ncomp; i++) - if (comps[i].neighbors) - ccv_array_push(result_seq2, &comps[i]); - - ccv_array_free(result_seq); - ccfree(comps); - } - else - { - result_seq2 = result_seq; - } - - for (i = 1; i < scale_upto + next * 2; i++) - ccv_matrix_free(pyr[i * 4]); - if (params.accurate) - for (i = next * 2; i < scale_upto + next * 2; i++) - { - ccv_matrix_free(pyr[i * 4 + 1]); - ccv_matrix_free(pyr[i * 4 + 2]); - ccv_matrix_free(pyr[i * 4 + 3]); - } - if (params.size.height != _cascade[0]->size.height || params.size.width != _cascade[0]->size.width) - ccv_matrix_free(pyr[0]); - - return result_seq2; -} - -ccv_bbf_classifier_cascade_t *ccv_bbf_read_classifier_cascade(const char *directory) -{ - char buf[1024]; - sprintf(buf, "%s/cascade.txt", directory); - int s, i; - FILE *r = fopen(buf, "r"); - if (r == 0) - return 0; - ccv_bbf_classifier_cascade_t *cascade = (ccv_bbf_classifier_cascade_t *)ccmalloc(sizeof(ccv_bbf_classifier_cascade_t)); - s = fscanf(r, "%d %d %d", &cascade->count, &cascade->size.width, &cascade->size.height); - assert(s > 0); - cascade->stage_classifier = (ccv_bbf_stage_classifier_t *)ccmalloc(cascade->count * sizeof(ccv_bbf_stage_classifier_t)); - for (i = 0; i < cascade->count; i++) - { - sprintf(buf, "%s/stage-%d.txt", directory, i); - if (_ccv_read_bbf_stage_classifier(buf, &cascade->stage_classifier[i]) < 0) - { - cascade->count = i; - break; - } - } - fclose(r); - return cascade; -} - -ccv_bbf_classifier_cascade_t *ccv_bbf_classifier_cascade_read_binary(char *s) -{ - int i; - ccv_bbf_classifier_cascade_t *cascade = (ccv_bbf_classifier_cascade_t *)ccmalloc(sizeof(ccv_bbf_classifier_cascade_t)); - memcpy(&cascade->count, s, sizeof(cascade->count)); - s += sizeof(cascade->count); - memcpy(&cascade->size.width, s, sizeof(cascade->size.width)); - s += sizeof(cascade->size.width); - memcpy(&cascade->size.height, s, sizeof(cascade->size.height)); - s += sizeof(cascade->size.height); - ccv_bbf_stage_classifier_t *classifier = cascade->stage_classifier = (ccv_bbf_stage_classifier_t *)ccmalloc(cascade->count * sizeof(ccv_bbf_stage_classifier_t)); - for (i = 0; i < cascade->count; i++, classifier++) - { - memcpy(&classifier->count, s, sizeof(classifier->count)); - s += sizeof(classifier->count); - memcpy(&classifier->threshold, s, sizeof(classifier->threshold)); - s += sizeof(classifier->threshold); - classifier->feature = (ccv_bbf_feature_t *)ccmalloc(classifier->count * sizeof(ccv_bbf_feature_t)); - classifier->alpha = (float *)ccmalloc(classifier->count * 2 * sizeof(float)); - memcpy(classifier->feature, s, classifier->count * sizeof(ccv_bbf_feature_t)); - s += classifier->count * sizeof(ccv_bbf_feature_t); - memcpy(classifier->alpha, s, classifier->count * 2 * sizeof(float)); - s += classifier->count * 2 * sizeof(float); - } - return cascade; -} - -int ccv_bbf_classifier_cascade_write_binary(ccv_bbf_classifier_cascade_t *cascade, char *s, int slen) -{ - int i; - int len = sizeof(cascade->count) + sizeof(cascade->size.width) + sizeof(cascade->size.height); - ccv_bbf_stage_classifier_t *classifier = cascade->stage_classifier; - for (i = 0; i < cascade->count; i++, classifier++) - len += sizeof(classifier->count) + sizeof(classifier->threshold) + classifier->count * sizeof(ccv_bbf_feature_t) + classifier->count * 2 * sizeof(float); - if (slen >= len) - { - memcpy(s, &cascade->count, sizeof(cascade->count)); - s += sizeof(cascade->count); - memcpy(s, &cascade->size.width, sizeof(cascade->size.width)); - s += sizeof(cascade->size.width); - memcpy(s, &cascade->size.height, sizeof(cascade->size.height)); - s += sizeof(cascade->size.height); - classifier = cascade->stage_classifier; - for (i = 0; i < cascade->count; i++, classifier++) - { - memcpy(s, &classifier->count, sizeof(classifier->count)); - s += sizeof(classifier->count); - memcpy(s, &classifier->threshold, sizeof(classifier->threshold)); - s += sizeof(classifier->threshold); - memcpy(s, classifier->feature, classifier->count * sizeof(ccv_bbf_feature_t)); - s += classifier->count * sizeof(ccv_bbf_feature_t); - memcpy(s, classifier->alpha, classifier->count * 2 * sizeof(float)); - s += classifier->count * 2 * sizeof(float); - } - } - return len; -} - -void ccv_bbf_classifier_cascade_free(ccv_bbf_classifier_cascade_t *cascade) -{ - int i; - for (i = 0; i < cascade->count; ++i) - { - ccfree(cascade->stage_classifier[i].feature); - ccfree(cascade->stage_classifier[i].alpha); - } - ccfree(cascade->stage_classifier); - ccfree(cascade); -} \ No newline at end of file diff --git a/test/source.c++ b/test/source.c++ index 6a6b390..97e29e8 100644 --- a/test/source.c++ +++ b/test/source.c++ @@ -88,1415 +88,4 @@ static int _ccv_read_bbf_stage_classifier(const char* file, ccv_bbf_stage_classi return 0; } -#ifdef HAVE_GSL - -static unsigned int _ccv_bbf_time_measure() -{ - struct timeval tv; - gettimeofday(&tv, 0); - return tv.tv_sec * 1000000 + tv.tv_usec; -} - -#define less_than(a, b, aux) ((a) < (b)) -CCV_IMPLEMENT_QSORT(_ccv_sort_32f, float, less_than) -#undef less_than - -static void _ccv_bbf_eval_data(ccv_bbf_stage_classifier_t* classifier, unsigned char** posdata, int posnum, unsigned char** negdata, int negnum, ccv_size_t size, float* peval, float* neval) -{ - int i, j; - int steps[] = { _ccv_width_padding(size.width), - _ccv_width_padding(size.width >> 1), - _ccv_width_padding(size.width >> 2) }; - int isizs0 = steps[0] * size.height; - int isizs01 = isizs0 + steps[1] * (size.height >> 1); - for (i = 0; i < posnum; i++) - { - unsigned char* u8[] = { posdata[i], posdata[i] + isizs0, posdata[i] + isizs01 }; - float sum = 0; - float* alpha = classifier->alpha; - ccv_bbf_feature_t* feature = classifier->feature; - for (j = 0; j < classifier->count; ++j, alpha += 2, ++feature) - sum += alpha[_ccv_run_bbf_feature(feature, steps, u8)]; - peval[i] = sum; - } - for (i = 0; i < negnum; i++) - { - unsigned char* u8[] = { negdata[i], negdata[i] + isizs0, negdata[i] + isizs01 }; - float sum = 0; - float* alpha = classifier->alpha; - ccv_bbf_feature_t* feature = classifier->feature; - for (j = 0; j < classifier->count; ++j, alpha += 2, ++feature) - sum += alpha[_ccv_run_bbf_feature(feature, steps, u8)]; - neval[i] = sum; - } -} - -static int _ccv_prune_positive_data(ccv_bbf_classifier_cascade_t* cascade, unsigned char** posdata, int posnum, ccv_size_t size) -{ - float* peval = (float*)ccmalloc(posnum * sizeof(float)); - int i, j, k, rpos = posnum; - for (i = 0; i < cascade->count; i++) - { - _ccv_bbf_eval_data(cascade->stage_classifier + i, posdata, rpos, 0, 0, size, peval, 0); - k = 0; - for (j = 0; j < rpos; j++) - if (peval[j] >= cascade->stage_classifier[i].threshold) - { - posdata[k] = posdata[j]; - ++k; - } else { - ccfree(posdata[j]); - } - rpos = k; - } - ccfree(peval); - return rpos; -} - -static int _ccv_prepare_background_data(ccv_bbf_classifier_cascade_t* cascade, char** bgfiles, int bgnum, unsigned char** negdata, int negnum) -{ - int t, i, j, k, q; - int negperbg; - int negtotal = 0; - int steps[] = { _ccv_width_padding(cascade->size.width), - _ccv_width_padding(cascade->size.width >> 1), - _ccv_width_padding(cascade->size.width >> 2) }; - int isizs0 = steps[0] * cascade->size.height; - int isizs1 = steps[1] * (cascade->size.height >> 1); - int isizs2 = steps[2] * (cascade->size.height >> 2); - int* idcheck = (int*)ccmalloc(negnum * sizeof(int)); - - gsl_rng_env_setup(); - - gsl_rng* rng = gsl_rng_alloc(gsl_rng_default); - gsl_rng_set(rng, (unsigned long int)idcheck); - - ccv_size_t imgsz = cascade->size; - int rneg = negtotal; - for (t = 0; negtotal < negnum; t++) - { - PRINT(CCV_CLI_INFO, "preparing negative data ... 0%%"); - for (i = 0; i < bgnum; i++) - { - negperbg = (t < 2) ? (negnum - negtotal) / (bgnum - i) + 1 : negnum - negtotal; - ccv_dense_matrix_t* image = 0; - ccv_read(bgfiles[i], &image, CCV_IO_GRAY | CCV_IO_ANY_FILE); - assert((image->type & CCV_C1) && (image->type & CCV_8U)); - if (image == 0) - { - PRINT(CCV_CLI_ERROR, "\n%s file corrupted\n", bgfiles[i]); - continue; - } - if (t % 2 != 0) - ccv_flip(image, 0, 0, CCV_FLIP_X); - if (t % 4 >= 2) - ccv_flip(image, 0, 0, CCV_FLIP_Y); - ccv_bbf_param_t params = { .interval = 3, .min_neighbors = 0, .accurate = 1, .flags = 0, .size = cascade->size }; - ccv_array_t* detected = ccv_bbf_detect_objects(image, &cascade, 1, params); - memset(idcheck, 0, ccv_min(detected->rnum, negperbg) * sizeof(int)); - for (j = 0; j < ccv_min(detected->rnum, negperbg); j++) - { - int r = gsl_rng_uniform_int(rng, detected->rnum); - int flag = 1; - ccv_rect_t* rect = (ccv_rect_t*)ccv_array_get(detected, r); - while (flag) { - flag = 0; - for (k = 0; k < j; k++) - if (r == idcheck[k]) - { - flag = 1; - r = gsl_rng_uniform_int(rng, detected->rnum); - break; - } - rect = (ccv_rect_t*)ccv_array_get(detected, r); - if ((rect->x < 0) || (rect->y < 0) || (rect->width + rect->x > image->cols) || (rect->height + rect->y > image->rows)) - { - flag = 1; - r = gsl_rng_uniform_int(rng, detected->rnum); - } - } - idcheck[j] = r; - ccv_dense_matrix_t* temp = 0; - ccv_dense_matrix_t* imgs0 = 0; - ccv_dense_matrix_t* imgs1 = 0; - ccv_dense_matrix_t* imgs2 = 0; - ccv_slice(image, (ccv_matrix_t**)&temp, 0, rect->y, rect->x, rect->height, rect->width); - ccv_resample(temp, &imgs0, 0, imgsz.height, imgsz.width, CCV_INTER_AREA); - assert(imgs0->step == steps[0]); - ccv_matrix_free(temp); - ccv_sample_down(imgs0, &imgs1, 0, 0, 0); - assert(imgs1->step == steps[1]); - ccv_sample_down(imgs1, &imgs2, 0, 0, 0); - assert(imgs2->step == steps[2]); - - negdata[negtotal] = (unsigned char*)ccmalloc(isizs0 + isizs1 + isizs2); - unsigned char* u8s0 = negdata[negtotal]; - unsigned char* u8s1 = negdata[negtotal] + isizs0; - unsigned char* u8s2 = negdata[negtotal] + isizs0 + isizs1; - unsigned char* u8[] = { u8s0, u8s1, u8s2 }; - memcpy(u8s0, imgs0->data.u8, imgs0->rows * imgs0->step); - ccv_matrix_free(imgs0); - memcpy(u8s1, imgs1->data.u8, imgs1->rows * imgs1->step); - ccv_matrix_free(imgs1); - memcpy(u8s2, imgs2->data.u8, imgs2->rows * imgs2->step); - ccv_matrix_free(imgs2); - - flag = 1; - ccv_bbf_stage_classifier_t* classifier = cascade->stage_classifier; - for (k = 0; k < cascade->count; ++k, ++classifier) - { - float sum = 0; - float* alpha = classifier->alpha; - ccv_bbf_feature_t* feature = classifier->feature; - for (q = 0; q < classifier->count; ++q, alpha += 2, ++feature) - sum += alpha[_ccv_run_bbf_feature(feature, steps, u8)]; - if (sum < classifier->threshold) - { - flag = 0; - break; - } - } - if (!flag) - ccfree(negdata[negtotal]); - else { - ++negtotal; - if (negtotal >= negnum) - break; - } - } - ccv_array_free(detected); - ccv_matrix_free(image); - ccv_drain_cache(); - PRINT(CCV_CLI_INFO, "\rpreparing negative data ... %2d%%", 100 * negtotal / negnum); - fflush(0); - if (negtotal >= negnum) - break; - } - if (rneg == negtotal) - break; - rneg = negtotal; - PRINT(CCV_CLI_INFO, "\nentering additional round %d\n", t + 1); - } - gsl_rng_free(rng); - ccfree(idcheck); - ccv_drain_cache(); - PRINT(CCV_CLI_INFO, "\n"); - return negtotal; -} - -static void _ccv_prepare_positive_data(ccv_dense_matrix_t** posimg, unsigned char** posdata, ccv_size_t size, int posnum) -{ - PRINT(CCV_CLI_INFO, "preparing positive data ... 0%%"); - int i; - for (i = 0; i < posnum; i++) - { - ccv_dense_matrix_t* imgs0 = posimg[i]; - ccv_dense_matrix_t* imgs1 = 0; - ccv_dense_matrix_t* imgs2 = 0; - assert((imgs0->type & CCV_C1) && (imgs0->type & CCV_8U) && imgs0->rows == size.height && imgs0->cols == size.width); - ccv_sample_down(imgs0, &imgs1, 0, 0, 0); - ccv_sample_down(imgs1, &imgs2, 0, 0, 0); - int isizs0 = imgs0->rows * imgs0->step; - int isizs1 = imgs1->rows * imgs1->step; - int isizs2 = imgs2->rows * imgs2->step; - - posdata[i] = (unsigned char*)ccmalloc(isizs0 + isizs1 + isizs2); - memcpy(posdata[i], imgs0->data.u8, isizs0); - memcpy(posdata[i] + isizs0, imgs1->data.u8, isizs1); - memcpy(posdata[i] + isizs0 + isizs1, imgs2->data.u8, isizs2); - - PRINT(CCV_CLI_INFO, "\rpreparing positive data ... %2d%%", 100 * (i + 1) / posnum); - fflush(0); - - ccv_matrix_free(imgs1); - ccv_matrix_free(imgs2); - } - ccv_drain_cache(); - PRINT(CCV_CLI_INFO, "\n"); -} - -typedef struct { - double fitness; - int pk, nk; - int age; - double error; - ccv_bbf_feature_t feature; -} ccv_bbf_gene_t; - -static inline void _ccv_bbf_genetic_fitness(ccv_bbf_gene_t* gene) -{ - gene->fitness = (1 - gene->error) * exp(-0.01 * gene->age) * exp((gene->pk + gene->nk) * log(1.015)); -} - -static inline int _ccv_bbf_exist_gene_feature(ccv_bbf_gene_t* gene, int x, int y, int z) -{ - int i; - for (i = 0; i < gene->pk; i++) - if (z == gene->feature.pz[i] && x == gene->feature.px[i] && y == gene->feature.py[i]) - return 1; - for (i = 0; i < gene->nk; i++) - if (z == gene->feature.nz[i] && x == gene->feature.nx[i] && y == gene->feature.ny[i]) - return 1; - return 0; -} - -static inline void _ccv_bbf_randomize_gene(gsl_rng* rng, ccv_bbf_gene_t* gene, int* rows, int* cols) -{ - int i; - do { - gene->pk = gsl_rng_uniform_int(rng, CCV_BBF_POINT_MAX - 1) + 1; - gene->nk = gsl_rng_uniform_int(rng, CCV_BBF_POINT_MAX - 1) + 1; - } while (gene->pk + gene->nk < CCV_BBF_POINT_MIN); /* a hard restriction of at least 3 points have to be examed */ - gene->feature.size = ccv_max(gene->pk, gene->nk); - gene->age = 0; - for (i = 0; i < CCV_BBF_POINT_MAX; i++) - { - gene->feature.pz[i] = -1; - gene->feature.nz[i] = -1; - } - int x, y, z; - for (i = 0; i < gene->pk; i++) - { - do { - z = gsl_rng_uniform_int(rng, 3); - x = gsl_rng_uniform_int(rng, cols[z]); - y = gsl_rng_uniform_int(rng, rows[z]); - } while (_ccv_bbf_exist_gene_feature(gene, x, y, z)); - gene->feature.pz[i] = z; - gene->feature.px[i] = x; - gene->feature.py[i] = y; - } - for (i = 0; i < gene->nk; i++) - { - do { - z = gsl_rng_uniform_int(rng, 3); - x = gsl_rng_uniform_int(rng, cols[z]); - y = gsl_rng_uniform_int(rng, rows[z]); - } while ( _ccv_bbf_exist_gene_feature(gene, x, y, z)); - gene->feature.nz[i] = z; - gene->feature.nx[i] = x; - gene->feature.ny[i] = y; - } -} - -static inline double _ccv_bbf_error_rate(ccv_bbf_feature_t* feature, unsigned char** posdata, int posnum, unsigned char** negdata, int negnum, ccv_size_t size, double* pw, double* nw) -{ - int i; - int steps[] = { _ccv_width_padding(size.width), - _ccv_width_padding(size.width >> 1), - _ccv_width_padding(size.width >> 2) }; - int isizs0 = steps[0] * size.height; - int isizs01 = isizs0 + steps[1] * (size.height >> 1); - double error = 0; - for (i = 0; i < posnum; i++) - { - unsigned char* u8[] = { posdata[i], posdata[i] + isizs0, posdata[i] + isizs01 }; - if (!_ccv_run_bbf_feature(feature, steps, u8)) - error += pw[i]; - } - for (i = 0; i < negnum; i++) - { - unsigned char* u8[] = { negdata[i], negdata[i] + isizs0, negdata[i] + isizs01 }; - if ( _ccv_run_bbf_feature(feature, steps, u8)) - error += nw[i]; - } - return error; -} - -#define less_than(fit1, fit2, aux) ((fit1).fitness >= (fit2).fitness) -static CCV_IMPLEMENT_QSORT(_ccv_bbf_genetic_qsort, ccv_bbf_gene_t, less_than) -#undef less_than - -static ccv_bbf_feature_t _ccv_bbf_genetic_optimize(unsigned char** posdata, int posnum, unsigned char** negdata, int negnum, int ftnum, ccv_size_t size, double* pw, double* nw) -{ - ccv_bbf_feature_t best; - /* seed (random method) */ - gsl_rng_env_setup(); - gsl_rng* rng = gsl_rng_alloc(gsl_rng_default); - union { unsigned long int li; double db; } dbli; - dbli.db = pw[0] + nw[0]; - gsl_rng_set(rng, dbli.li); - int i, j; - int pnum = ftnum * 100; - assert(pnum > 0); - ccv_bbf_gene_t* gene = (ccv_bbf_gene_t*)ccmalloc(pnum * sizeof(ccv_bbf_gene_t)); - int rows[] = { size.height, size.height >> 1, size.height >> 2 }; - int cols[] = { size.width, size.width >> 1, size.width >> 2 }; - for (i = 0; i < pnum; i++) - _ccv_bbf_randomize_gene(rng, &gene[i], rows, cols); - unsigned int timer = _ccv_bbf_time_measure(); -#ifdef USE_OPENMP -#pragma omp parallel for private(i) schedule(dynamic) -#endif - for (i = 0; i < pnum; i++) - gene[i].error = _ccv_bbf_error_rate(&gene[i].feature, posdata, posnum, negdata, negnum, size, pw, nw); - timer = _ccv_bbf_time_measure() - timer; - for (i = 0; i < pnum; i++) - _ccv_bbf_genetic_fitness(&gene[i]); - double best_err = 1; - int rnum = ftnum * 39; /* number of randomize */ - int mnum = ftnum * 40; /* number of mutation */ - int hnum = ftnum * 20; /* number of hybrid */ - /* iteration stop crit : best no change in 40 iterations */ - int it = 0, t; - for (t = 0 ; it < 40; ++it, ++t) - { - int min_id = 0; - double min_err = gene[0].error; - for (i = 1; i < pnum; i++) - if (gene[i].error < min_err) - { - min_id = i; - min_err = gene[i].error; - } - min_err = gene[min_id].error = _ccv_bbf_error_rate(&gene[min_id].feature, posdata, posnum, negdata, negnum, size, pw, nw); - if (min_err < best_err) - { - best_err = min_err; - memcpy(&best, &gene[min_id].feature, sizeof(best)); - PRINT(CCV_CLI_INFO, "best bbf feature with error %f\n|-size: %d\n|-positive point: ", best_err, best.size); - for (i = 0; i < best.size; i++) - PRINT(CCV_CLI_INFO, "(%d %d %d), ", best.px[i], best.py[i], best.pz[i]); - PRINT(CCV_CLI_INFO, "\n|-negative point: "); - for (i = 0; i < best.size; i++) - PRINT(CCV_CLI_INFO, "(%d %d %d), ", best.nx[i], best.ny[i], best.nz[i]); - PRINT(CCV_CLI_INFO, "\n"); - it = 0; - } - PRINT(CCV_CLI_INFO, "minimum error achieved in round %d(%d) : %f with %d ms\n", t, it, min_err, timer / 1000); - _ccv_bbf_genetic_qsort(gene, pnum, 0); - for (i = 0; i < ftnum; i++) - ++gene[i].age; - for (i = ftnum; i < ftnum + mnum; i++) - { - int parent = gsl_rng_uniform_int(rng, ftnum); - memcpy(gene + i, gene + parent, sizeof(ccv_bbf_gene_t)); - /* three mutation strategy : 1. add, 2. remove, 3. refine */ - int pnm, pn = gsl_rng_uniform_int(rng, 2); - int* pnk[] = { &gene[i].pk, &gene[i].nk }; - int* pnx[] = { gene[i].feature.px, gene[i].feature.nx }; - int* pny[] = { gene[i].feature.py, gene[i].feature.ny }; - int* pnz[] = { gene[i].feature.pz, gene[i].feature.nz }; - int x, y, z; - int victim, decay = 1; - do { - switch (gsl_rng_uniform_int(rng, 3)) - { - case 0: /* add */ - if (gene[i].pk == CCV_BBF_POINT_MAX && gene[i].nk == CCV_BBF_POINT_MAX) - break; - while (*pnk[pn] + 1 > CCV_BBF_POINT_MAX) - pn = gsl_rng_uniform_int(rng, 2); - do { - z = gsl_rng_uniform_int(rng, 3); - x = gsl_rng_uniform_int(rng, cols[z]); - y = gsl_rng_uniform_int(rng, rows[z]); - } while (_ccv_bbf_exist_gene_feature(&gene[i], x, y, z)); - pnz[pn][*pnk[pn]] = z; - pnx[pn][*pnk[pn]] = x; - pny[pn][*pnk[pn]] = y; - ++(*pnk[pn]); - gene[i].feature.size = ccv_max(gene[i].pk, gene[i].nk); - decay = gene[i].age = 0; - break; - case 1: /* remove */ - if (gene[i].pk + gene[i].nk <= CCV_BBF_POINT_MIN) /* at least 3 points have to be examed */ - break; - while (*pnk[pn] - 1 <= 0) // || *pnk[pn] + *pnk[!pn] - 1 < CCV_BBF_POINT_MIN) - pn = gsl_rng_uniform_int(rng, 2); - victim = gsl_rng_uniform_int(rng, *pnk[pn]); - for (j = victim; j < *pnk[pn] - 1; j++) - { - pnz[pn][j] = pnz[pn][j + 1]; - pnx[pn][j] = pnx[pn][j + 1]; - pny[pn][j] = pny[pn][j + 1]; - } - pnz[pn][*pnk[pn] - 1] = -1; - --(*pnk[pn]); - gene[i].feature.size = ccv_max(gene[i].pk, gene[i].nk); - decay = gene[i].age = 0; - break; - case 2: /* refine */ - pnm = gsl_rng_uniform_int(rng, *pnk[pn]); - do { - z = gsl_rng_uniform_int(rng, 3); - x = gsl_rng_uniform_int(rng, cols[z]); - y = gsl_rng_uniform_int(rng, rows[z]); - } while (_ccv_bbf_exist_gene_feature(&gene[i], x, y, z)); - pnz[pn][pnm] = z; - pnx[pn][pnm] = x; - pny[pn][pnm] = y; - decay = gene[i].age = 0; - break; - } - } while (decay); - } - for (i = ftnum + mnum; i < ftnum + mnum + hnum; i++) - { - /* hybrid strategy: taking positive points from dad, negative points from mum */ - int dad, mum; - do { - dad = gsl_rng_uniform_int(rng, ftnum); - mum = gsl_rng_uniform_int(rng, ftnum); - } while (dad == mum || gene[dad].pk + gene[mum].nk < CCV_BBF_POINT_MIN); /* at least 3 points have to be examed */ - for (j = 0; j < CCV_BBF_POINT_MAX; j++) - { - gene[i].feature.pz[j] = -1; - gene[i].feature.nz[j] = -1; - } - gene[i].pk = gene[dad].pk; - for (j = 0; j < gene[i].pk; j++) - { - gene[i].feature.pz[j] = gene[dad].feature.pz[j]; - gene[i].feature.px[j] = gene[dad].feature.px[j]; - gene[i].feature.py[j] = gene[dad].feature.py[j]; - } - gene[i].nk = gene[mum].nk; - for (j = 0; j < gene[i].nk; j++) - { - gene[i].feature.nz[j] = gene[mum].feature.nz[j]; - gene[i].feature.nx[j] = gene[mum].feature.nx[j]; - gene[i].feature.ny[j] = gene[mum].feature.ny[j]; - } - gene[i].feature.size = ccv_max(gene[i].pk, gene[i].nk); - gene[i].age = 0; - } - for (i = ftnum + mnum + hnum; i < ftnum + mnum + hnum + rnum; i++) - _ccv_bbf_randomize_gene(rng, &gene[i], rows, cols); - timer = _ccv_bbf_time_measure(); -#ifdef USE_OPENMP -#pragma omp parallel for private(i) schedule(dynamic) -#endif - for (i = 0; i < pnum; i++) - gene[i].error = _ccv_bbf_error_rate(&gene[i].feature, posdata, posnum, negdata, negnum, size, pw, nw); - timer = _ccv_bbf_time_measure() - timer; - for (i = 0; i < pnum; i++) - _ccv_bbf_genetic_fitness(&gene[i]); - } - ccfree(gene); - gsl_rng_free(rng); - return best; -} - -#define less_than(fit1, fit2, aux) ((fit1).error < (fit2).error) -static CCV_IMPLEMENT_QSORT(_ccv_bbf_best_qsort, ccv_bbf_gene_t, less_than) -#undef less_than - -static ccv_bbf_gene_t _ccv_bbf_best_gene(ccv_bbf_gene_t* gene, int pnum, int point_min, unsigned char** posdata, int posnum, unsigned char** negdata, int negnum, ccv_size_t size, double* pw, double* nw) -{ - int i; - unsigned int timer = _ccv_bbf_time_measure(); -#ifdef USE_OPENMP -#pragma omp parallel for private(i) schedule(dynamic) -#endif - for (i = 0; i < pnum; i++) - gene[i].error = _ccv_bbf_error_rate(&gene[i].feature, posdata, posnum, negdata, negnum, size, pw, nw); - timer = _ccv_bbf_time_measure() - timer; - _ccv_bbf_best_qsort(gene, pnum, 0); - int min_id = 0; - double min_err = gene[0].error; - for (i = 0; i < pnum; i++) - if (gene[i].nk + gene[i].pk >= point_min) - { - min_id = i; - min_err = gene[i].error; - break; - } - PRINT(CCV_CLI_INFO, "local best bbf feature with error %f\n|-size: %d\n|-positive point: ", min_err, gene[min_id].feature.size); - for (i = 0; i < gene[min_id].feature.size; i++) - PRINT(CCV_CLI_INFO, "(%d %d %d), ", gene[min_id].feature.px[i], gene[min_id].feature.py[i], gene[min_id].feature.pz[i]); - PRINT(CCV_CLI_INFO, "\n|-negative point: "); - for (i = 0; i < gene[min_id].feature.size; i++) - PRINT(CCV_CLI_INFO, "(%d %d %d), ", gene[min_id].feature.nx[i], gene[min_id].feature.ny[i], gene[min_id].feature.nz[i]); - PRINT(CCV_CLI_INFO, "\nthe computation takes %d ms\n", timer / 1000); - return gene[min_id]; -} - -static ccv_bbf_feature_t _ccv_bbf_convex_optimize(unsigned char** posdata, int posnum, unsigned char** negdata, int negnum, ccv_bbf_feature_t* best_feature, ccv_size_t size, double* pw, double* nw) -{ - ccv_bbf_gene_t best_gene; - /* seed (random method) */ - gsl_rng_env_setup(); - gsl_rng* rng = gsl_rng_alloc(gsl_rng_default); - union { unsigned long int li; double db; } dbli; - dbli.db = pw[0] + nw[0]; - gsl_rng_set(rng, dbli.li); - int i, j, k, q, p, g, t; - int rows[] = { size.height, size.height >> 1, size.height >> 2 }; - int cols[] = { size.width, size.width >> 1, size.width >> 2 }; - int pnum = rows[0] * cols[0] + rows[1] * cols[1] + rows[2] * cols[2]; - ccv_bbf_gene_t* gene = (ccv_bbf_gene_t*)ccmalloc((pnum * (CCV_BBF_POINT_MAX * 2 + 1) * 2 + CCV_BBF_POINT_MAX * 2 + 1) * sizeof(ccv_bbf_gene_t)); - if (best_feature == 0) - { - /* bootstrapping the best feature, start from two pixels, one for positive, one for negative - * the bootstrapping process go like this: first, it will assign a random pixel as positive - * and enumerate every possible pixel as negative, and pick the best one. Then, enumerate every - * possible pixel as positive, and pick the best one, until it converges */ - memset(&best_gene, 0, sizeof(ccv_bbf_gene_t)); - for (i = 0; i < CCV_BBF_POINT_MAX; i++) - best_gene.feature.pz[i] = best_gene.feature.nz[i] = -1; - best_gene.pk = 1; - best_gene.nk = 0; - best_gene.feature.size = 1; - best_gene.feature.pz[0] = gsl_rng_uniform_int(rng, 3); - best_gene.feature.px[0] = gsl_rng_uniform_int(rng, cols[best_gene.feature.pz[0]]); - best_gene.feature.py[0] = gsl_rng_uniform_int(rng, rows[best_gene.feature.pz[0]]); - for (t = 0; ; ++t) - { - g = 0; - if (t % 2 == 0) - { - for (i = 0; i < 3; i++) - for (j = 0; j < cols[i]; j++) - for (k = 0; k < rows[i]; k++) - if (i != best_gene.feature.pz[0] || j != best_gene.feature.px[0] || k != best_gene.feature.py[0]) - { - gene[g] = best_gene; - gene[g].pk = gene[g].nk = 1; - gene[g].feature.nz[0] = i; - gene[g].feature.nx[0] = j; - gene[g].feature.ny[0] = k; - g++; - } - } else { - for (i = 0; i < 3; i++) - for (j = 0; j < cols[i]; j++) - for (k = 0; k < rows[i]; k++) - if (i != best_gene.feature.nz[0] || j != best_gene.feature.nx[0] || k != best_gene.feature.ny[0]) - { - gene[g] = best_gene; - gene[g].pk = gene[g].nk = 1; - gene[g].feature.pz[0] = i; - gene[g].feature.px[0] = j; - gene[g].feature.py[0] = k; - g++; - } - } - PRINT(CCV_CLI_INFO, "bootstrapping round : %d\n", t); - ccv_bbf_gene_t local_gene = _ccv_bbf_best_gene(gene, g, 2, posdata, posnum, negdata, negnum, size, pw, nw); - if (local_gene.error >= best_gene.error - 1e-10) - break; - best_gene = local_gene; - } - } else { - best_gene.feature = *best_feature; - best_gene.pk = best_gene.nk = best_gene.feature.size; - for (i = 0; i < CCV_BBF_POINT_MAX; i++) - if (best_feature->pz[i] == -1) - { - best_gene.pk = i; - break; - } - for (i = 0; i < CCV_BBF_POINT_MAX; i++) - if (best_feature->nz[i] == -1) - { - best_gene.nk = i; - break; - } - } - /* after bootstrapping, the float search technique will do the following permutations: - * a). add a new point to positive or negative - * b). remove a point from positive or negative - * c). move an existing point in positive or negative to another position - * the three rules applied exhaustively, no heuristic used. */ - for (t = 0; ; ++t) - { - g = 0; - for (i = 0; i < 3; i++) - for (j = 0; j < cols[i]; j++) - for (k = 0; k < rows[i]; k++) - if (!_ccv_bbf_exist_gene_feature(&best_gene, j, k, i)) - { - /* add positive point */ - if (best_gene.pk < CCV_BBF_POINT_MAX - 1) - { - gene[g] = best_gene; - gene[g].feature.pz[gene[g].pk] = i; - gene[g].feature.px[gene[g].pk] = j; - gene[g].feature.py[gene[g].pk] = k; - gene[g].pk++; - gene[g].feature.size = ccv_max(gene[g].pk, gene[g].nk); - g++; - } - /* add negative point */ - if (best_gene.nk < CCV_BBF_POINT_MAX - 1) - { - gene[g] = best_gene; - gene[g].feature.nz[gene[g].nk] = i; - gene[g].feature.nx[gene[g].nk] = j; - gene[g].feature.ny[gene[g].nk] = k; - gene[g].nk++; - gene[g].feature.size = ccv_max(gene[g].pk, gene[g].nk); - g++; - } - /* refine positive point */ - for (q = 0; q < best_gene.pk; q++) - { - gene[g] = best_gene; - gene[g].feature.pz[q] = i; - gene[g].feature.px[q] = j; - gene[g].feature.py[q] = k; - g++; - } - /* add positive point, remove negative point */ - if (best_gene.pk < CCV_BBF_POINT_MAX - 1 && best_gene.nk > 1) - { - for (q = 0; q < best_gene.nk; q++) - { - gene[g] = best_gene; - gene[g].feature.pz[gene[g].pk] = i; - gene[g].feature.px[gene[g].pk] = j; - gene[g].feature.py[gene[g].pk] = k; - gene[g].pk++; - for (p = q; p < best_gene.nk - 1; p++) - { - gene[g].feature.nz[p] = gene[g].feature.nz[p + 1]; - gene[g].feature.nx[p] = gene[g].feature.nx[p + 1]; - gene[g].feature.ny[p] = gene[g].feature.ny[p + 1]; - } - gene[g].feature.nz[gene[g].nk - 1] = -1; - gene[g].nk--; - gene[g].feature.size = ccv_max(gene[g].pk, gene[g].nk); - g++; - } - } - /* refine negative point */ - for (q = 0; q < best_gene.nk; q++) - { - gene[g] = best_gene; - gene[g].feature.nz[q] = i; - gene[g].feature.nx[q] = j; - gene[g].feature.ny[q] = k; - g++; - } - /* add negative point, remove positive point */ - if (best_gene.pk > 1 && best_gene.nk < CCV_BBF_POINT_MAX - 1) - { - for (q = 0; q < best_gene.pk; q++) - { - gene[g] = best_gene; - gene[g].feature.nz[gene[g].nk] = i; - gene[g].feature.nx[gene[g].nk] = j; - gene[g].feature.ny[gene[g].nk] = k; - gene[g].nk++; - for (p = q; p < best_gene.pk - 1; p++) - { - gene[g].feature.pz[p] = gene[g].feature.pz[p + 1]; - gene[g].feature.px[p] = gene[g].feature.px[p + 1]; - gene[g].feature.py[p] = gene[g].feature.py[p + 1]; - } - gene[g].feature.pz[gene[g].pk - 1] = -1; - gene[g].pk--; - gene[g].feature.size = ccv_max(gene[g].pk, gene[g].nk); - g++; - } - } - } - if (best_gene.pk > 1) - for (q = 0; q < best_gene.pk; q++) - { - gene[g] = best_gene; - for (i = q; i < best_gene.pk - 1; i++) - { - gene[g].feature.pz[i] = gene[g].feature.pz[i + 1]; - gene[g].feature.px[i] = gene[g].feature.px[i + 1]; - gene[g].feature.py[i] = gene[g].feature.py[i + 1]; - } - gene[g].feature.pz[gene[g].pk - 1] = -1; - gene[g].pk--; - gene[g].feature.size = ccv_max(gene[g].pk, gene[g].nk); - g++; - } - if (best_gene.nk > 1) - for (q = 0; q < best_gene.nk; q++) - { - gene[g] = best_gene; - for (i = q; i < best_gene.nk - 1; i++) - { - gene[g].feature.nz[i] = gene[g].feature.nz[i + 1]; - gene[g].feature.nx[i] = gene[g].feature.nx[i + 1]; - gene[g].feature.ny[i] = gene[g].feature.ny[i + 1]; - } - gene[g].feature.nz[gene[g].nk - 1] = -1; - gene[g].nk--; - gene[g].feature.size = ccv_max(gene[g].pk, gene[g].nk); - g++; - } - gene[g] = best_gene; - g++; - PRINT(CCV_CLI_INFO, "float search round : %d\n", t); - ccv_bbf_gene_t local_gene = _ccv_bbf_best_gene(gene, g, CCV_BBF_POINT_MIN, posdata, posnum, negdata, negnum, size, pw, nw); - if (local_gene.error >= best_gene.error - 1e-10) - break; - best_gene = local_gene; - } - ccfree(gene); - gsl_rng_free(rng); - return best_gene.feature; -} - -static int _ccv_write_bbf_stage_classifier(const char* file, ccv_bbf_stage_classifier_t* classifier) -{ - FILE* w = fopen(file, "wb"); - if (w == 0) return -1; - fprintf(w, "%d\n", classifier->count); - union { float fl; int i; } fli; - fli.fl = classifier->threshold; - fprintf(w, "%d\n", fli.i); - int i, j; - for (i = 0; i < classifier->count; i++) - { - fprintf(w, "%d\n", classifier->feature[i].size); - for (j = 0; j < classifier->feature[i].size; j++) - { - fprintf(w, "%d %d %d\n", classifier->feature[i].px[j], classifier->feature[i].py[j], classifier->feature[i].pz[j]); - fprintf(w, "%d %d %d\n", classifier->feature[i].nx[j], classifier->feature[i].ny[j], classifier->feature[i].nz[j]); - } - union { float fl; int i; } flia, flib; - flia.fl = classifier->alpha[i * 2]; - flib.fl = classifier->alpha[i * 2 + 1]; - fprintf(w, "%d %d\n", flia.i, flib.i); - } - fclose(w); - return 0; -} - -static int _ccv_read_background_data(const char* file, unsigned char** negdata, int* negnum, ccv_size_t size) -{ - int stat = 0; - FILE* r = fopen(file, "rb"); - if (r == 0) return -1; - stat |= fread(negnum, sizeof(int), 1, r); - int i; - int isizs012 = _ccv_width_padding(size.width) * size.height + - _ccv_width_padding(size.width >> 1) * (size.height >> 1) + - _ccv_width_padding(size.width >> 2) * (size.height >> 2); - for (i = 0; i < *negnum; i++) - { - negdata[i] = (unsigned char*)ccmalloc(isizs012); - stat |= fread(negdata[i], 1, isizs012, r); - } - fclose(r); - return 0; -} - -static int _ccv_write_background_data(const char* file, unsigned char** negdata, int negnum, ccv_size_t size) -{ - FILE* w = fopen(file, "w"); - if (w == 0) return -1; - fwrite(&negnum, sizeof(int), 1, w); - int i; - int isizs012 = _ccv_width_padding(size.width) * size.height + - _ccv_width_padding(size.width >> 1) * (size.height >> 1) + - _ccv_width_padding(size.width >> 2) * (size.height >> 2); - for (i = 0; i < negnum; i++) - fwrite(negdata[i], 1, isizs012, w); - fclose(w); - return 0; -} - -static int _ccv_resume_bbf_cascade_training_state(const char* file, int* i, int* k, int* bg, double* pw, double* nw, int posnum, int negnum) -{ - int stat = 0; - FILE* r = fopen(file, "r"); - if (r == 0) return -1; - stat |= fscanf(r, "%d %d %d", i, k, bg); - int j; - union { double db; int i[2]; } dbi; - for (j = 0; j < posnum; j++) - { - stat |= fscanf(r, "%d %d", &dbi.i[0], &dbi.i[1]); - pw[j] = dbi.db; - } - for (j = 0; j < negnum; j++) - { - stat |= fscanf(r, "%d %d", &dbi.i[0], &dbi.i[1]); - nw[j] = dbi.db; - } - fclose(r); - return 0; -} - -static int _ccv_save_bbf_cacade_training_state(const char* file, int i, int k, int bg, double* pw, double* nw, int posnum, int negnum) -{ - FILE* w = fopen(file, "w"); - if (w == 0) return -1; - fprintf(w, "%d %d %d\n", i, k, bg); - int j; - union { double db; int i[2]; } dbi; - for (j = 0; j < posnum; ++j) - { - dbi.db = pw[j]; - fprintf(w, "%d %d ", dbi.i[0], dbi.i[1]); - } - fprintf(w, "\n"); - for (j = 0; j < negnum; ++j) - { - dbi.db = nw[j]; - fprintf(w, "%d %d ", dbi.i[0], dbi.i[1]); - } - fprintf(w, "\n"); - fclose(w); - return 0; -} - -void ccv_bbf_classifier_cascade_new(ccv_dense_matrix_t** posimg, int posnum, char** bgfiles, int bgnum, int negnum, ccv_size_t size, const char* dir, ccv_bbf_new_param_t params) -{ - int i, j, k; - /* allocate memory for usage */ - ccv_bbf_classifier_cascade_t* cascade = (ccv_bbf_classifier_cascade_t*)ccmalloc(sizeof(ccv_bbf_classifier_cascade_t)); - cascade->count = 0; - cascade->size = size; - cascade->stage_classifier = (ccv_bbf_stage_classifier_t*)ccmalloc(sizeof(ccv_bbf_stage_classifier_t)); - unsigned char** posdata = (unsigned char**)ccmalloc(posnum * sizeof(unsigned char*)); - unsigned char** negdata = (unsigned char**)ccmalloc(negnum * sizeof(unsigned char*)); - double* pw = (double*)ccmalloc(posnum * sizeof(double)); - double* nw = (double*)ccmalloc(negnum * sizeof(double)); - float* peval = (float*)ccmalloc(posnum * sizeof(float)); - float* neval = (float*)ccmalloc(negnum * sizeof(float)); - double inv_balance_k = 1. / params.balance_k; - /* balance factor k, and weighted with 0.01 */ - params.balance_k *= 0.01; - inv_balance_k *= 0.01; - - int steps[] = { _ccv_width_padding(cascade->size.width), - _ccv_width_padding(cascade->size.width >> 1), - _ccv_width_padding(cascade->size.width >> 2) }; - int isizs0 = steps[0] * cascade->size.height; - int isizs01 = isizs0 + steps[1] * (cascade->size.height >> 1); - - i = 0; - k = 0; - int bg = 0; - int cacheK = 10; - /* state resume code */ - char buf[1024]; - sprintf(buf, "%s/stat.txt", dir); - _ccv_resume_bbf_cascade_training_state(buf, &i, &k, &bg, pw, nw, posnum, negnum); - if (i > 0) - { - cascade->count = i; - ccfree(cascade->stage_classifier); - cascade->stage_classifier = (ccv_bbf_stage_classifier_t*)ccmalloc(i * sizeof(ccv_bbf_stage_classifier_t)); - for (j = 0; j < i; j++) - { - sprintf(buf, "%s/stage-%d.txt", dir, j); - _ccv_read_bbf_stage_classifier(buf, &cascade->stage_classifier[j]); - } - } - if (k > 0) - cacheK = k; - int rpos, rneg = 0; - if (bg) - { - sprintf(buf, "%s/negs.txt", dir); - _ccv_read_background_data(buf, negdata, &rneg, cascade->size); - } - - for (; i < params.layer; i++) - { - if (!bg) - { - rneg = _ccv_prepare_background_data(cascade, bgfiles, bgnum, negdata, negnum); - /* save state of background data */ - sprintf(buf, "%s/negs.txt", dir); - _ccv_write_background_data(buf, negdata, rneg, cascade->size); - bg = 1; - } - double totalw; - /* save state of cascade : level, weight etc. */ - sprintf(buf, "%s/stat.txt", dir); - _ccv_save_bbf_cacade_training_state(buf, i, k, bg, pw, nw, posnum, negnum); - ccv_bbf_stage_classifier_t classifier; - if (k > 0) - { - /* resume state of classifier */ - sprintf( buf, "%s/stage-%d.txt", dir, i ); - _ccv_read_bbf_stage_classifier(buf, &classifier); - } else { - /* initialize classifier */ - for (j = 0; j < posnum; j++) - pw[j] = params.balance_k; - for (j = 0; j < rneg; j++) - nw[j] = inv_balance_k; - classifier.count = k; - classifier.threshold = 0; - classifier.feature = (ccv_bbf_feature_t*)ccmalloc(cacheK * sizeof(ccv_bbf_feature_t)); - classifier.alpha = (float*)ccmalloc(cacheK * 2 * sizeof(float)); - } - _ccv_prepare_positive_data(posimg, posdata, cascade->size, posnum); - rpos = _ccv_prune_positive_data(cascade, posdata, posnum, cascade->size); - PRINT(CCV_CLI_INFO, "%d postivie data and %d negative data in training\n", rpos, rneg); - /* reweight to 1.00 */ - totalw = 0; - for (j = 0; j < rpos; j++) - totalw += pw[j]; - for (j = 0; j < rneg; j++) - totalw += nw[j]; - for (j = 0; j < rpos; j++) - pw[j] = pw[j] / totalw; - for (j = 0; j < rneg; j++) - nw[j] = nw[j] / totalw; - for (; ; k++) - { - /* get overall true-positive, false-positive rate and threshold */ - double tp = 0, fp = 0, etp = 0, efp = 0; - _ccv_bbf_eval_data(&classifier, posdata, rpos, negdata, rneg, cascade->size, peval, neval); - _ccv_sort_32f(peval, rpos, 0); - classifier.threshold = peval[(int)((1. - params.pos_crit) * rpos)] - 1e-6; - for (j = 0; j < rpos; j++) - { - if (peval[j] >= 0) - ++tp; - if (peval[j] >= classifier.threshold) - ++etp; - } - tp /= rpos; etp /= rpos; - for (j = 0; j < rneg; j++) - { - if (neval[j] >= 0) - ++fp; - if (neval[j] >= classifier.threshold) - ++efp; - } - fp /= rneg; efp /= rneg; - PRINT(CCV_CLI_INFO, "stage classifier real TP rate : %f, FP rate : %f\n", tp, fp); - PRINT(CCV_CLI_INFO, "stage classifier TP rate : %f, FP rate : %f at threshold : %f\n", etp, efp, classifier.threshold); - if (k > 0) - { - /* save classifier state */ - sprintf(buf, "%s/stage-%d.txt", dir, i); - _ccv_write_bbf_stage_classifier(buf, &classifier); - sprintf(buf, "%s/stat.txt", dir); - _ccv_save_bbf_cacade_training_state(buf, i, k, bg, pw, nw, posnum, negnum); - } - if (etp > params.pos_crit && efp < params.neg_crit) - break; - /* TODO: more post-process is needed in here */ - - /* select the best feature in current distribution through genetic algorithm optimization */ - ccv_bbf_feature_t best; - if (params.optimizer == CCV_BBF_GENETIC_OPT) - { - best = _ccv_bbf_genetic_optimize(posdata, rpos, negdata, rneg, params.feature_number, cascade->size, pw, nw); - } else if (params.optimizer == CCV_BBF_FLOAT_OPT) { - best = _ccv_bbf_convex_optimize(posdata, rpos, negdata, rneg, 0, cascade->size, pw, nw); - } else { - best = _ccv_bbf_genetic_optimize(posdata, rpos, negdata, rneg, params.feature_number, cascade->size, pw, nw); - best = _ccv_bbf_convex_optimize(posdata, rpos, negdata, rneg, &best, cascade->size, pw, nw); - } - double err = _ccv_bbf_error_rate(&best, posdata, rpos, negdata, rneg, cascade->size, pw, nw); - double rw = (1 - err) / err; - totalw = 0; - /* reweight */ - for (j = 0; j < rpos; j++) - { - unsigned char* u8[] = { posdata[j], posdata[j] + isizs0, posdata[j] + isizs01 }; - if (!_ccv_run_bbf_feature(&best, steps, u8)) - pw[j] *= rw; - pw[j] *= params.balance_k; - totalw += pw[j]; - } - for (j = 0; j < rneg; j++) - { - unsigned char* u8[] = { negdata[j], negdata[j] + isizs0, negdata[j] + isizs01 }; - if (_ccv_run_bbf_feature(&best, steps, u8)) - nw[j] *= rw; - nw[j] *= inv_balance_k; - totalw += nw[j]; - } - for (j = 0; j < rpos; j++) - pw[j] = pw[j] / totalw; - for (j = 0; j < rneg; j++) - nw[j] = nw[j] / totalw; - double c = log(rw); - PRINT(CCV_CLI_INFO, "coefficient of feature %d: %f\n", k + 1, c); - classifier.count = k + 1; - /* resizing classifier */ - if (k >= cacheK) - { - ccv_bbf_feature_t* feature = (ccv_bbf_feature_t*)ccmalloc(cacheK * 2 * sizeof(ccv_bbf_feature_t)); - memcpy(feature, classifier.feature, cacheK * sizeof(ccv_bbf_feature_t)); - ccfree(classifier.feature); - float* alpha = (float*)ccmalloc(cacheK * 4 * sizeof(float)); - memcpy(alpha, classifier.alpha, cacheK * 2 * sizeof(float)); - ccfree(classifier.alpha); - classifier.feature = feature; - classifier.alpha = alpha; - cacheK *= 2; - } - /* setup new feature */ - classifier.feature[k] = best; - classifier.alpha[k * 2] = -c; - classifier.alpha[k * 2 + 1] = c; - } - cascade->count = i + 1; - ccv_bbf_stage_classifier_t* stage_classifier = (ccv_bbf_stage_classifier_t*)ccmalloc(cascade->count * sizeof(ccv_bbf_stage_classifier_t)); - memcpy(stage_classifier, cascade->stage_classifier, i * sizeof(ccv_bbf_stage_classifier_t)); - ccfree(cascade->stage_classifier); - stage_classifier[i] = classifier; - cascade->stage_classifier = stage_classifier; - k = 0; - bg = 0; - for (j = 0; j < rpos; j++) - ccfree(posdata[j]); - for (j = 0; j < rneg; j++) - ccfree(negdata[j]); - } - - ccfree(neval); - ccfree(peval); - ccfree(nw); - ccfree(pw); - ccfree(negdata); - ccfree(posdata); - ccfree(cascade); -} -#else -void ccv_bbf_classifier_cascade_new(ccv_dense_matrix_t** posimg, int posnum, char** bgfiles, int bgnum, int negnum, ccv_size_t size, const char* dir, ccv_bbf_new_param_t params) -{ - fprintf(stderr, " ccv_bbf_classifier_cascade_new requires libgsl support, please compile ccv with libgsl.\n"); -} -#endif - -static int _ccv_is_equal(const void* _r1, const void* _r2, void* data) -{ - const ccv_comp_t* r1 = (const ccv_comp_t*)_r1; - const ccv_comp_t* r2 = (const ccv_comp_t*)_r2; - int distance = (int)(r1->rect.width * 0.25 + 0.5); - - return r2->rect.x <= r1->rect.x + distance && - r2->rect.x >= r1->rect.x - distance && - r2->rect.y <= r1->rect.y + distance && - r2->rect.y >= r1->rect.y - distance && - r2->rect.width <= (int)(r1->rect.width * 1.5 + 0.5) && - (int)(r2->rect.width * 1.5 + 0.5) >= r1->rect.width; -} - -static int _ccv_is_equal_same_class(const void* _r1, const void* _r2, void* data) -{ - const ccv_comp_t* r1 = (const ccv_comp_t*)_r1; - const ccv_comp_t* r2 = (const ccv_comp_t*)_r2; - int distance = (int)(r1->rect.width * 0.25 + 0.5); - - return r2->classification.id == r1->classification.id && - r2->rect.x <= r1->rect.x + distance && - r2->rect.x >= r1->rect.x - distance && - r2->rect.y <= r1->rect.y + distance && - r2->rect.y >= r1->rect.y - distance && - r2->rect.width <= (int)(r1->rect.width * 1.5 + 0.5) && - (int)(r2->rect.width * 1.5 + 0.5) >= r1->rect.width; -} - -ccv_array_t* ccv_bbf_detect_objects(ccv_dense_matrix_t* a, ccv_bbf_classifier_cascade_t** _cascade, int count, ccv_bbf_param_t params) -{ - int hr = a->rows / params.size.height; - int wr = a->cols / params.size.width; - double scale = pow(2., 1. / (params.interval + 1.)); - int next = params.interval + 1; - int scale_upto = (int)(log((double)ccv_min(hr, wr)) / log(scale)); - ccv_dense_matrix_t** pyr = (ccv_dense_matrix_t**)alloca((scale_upto + next * 2) * 4 * sizeof(ccv_dense_matrix_t*)); - memset(pyr, 0, (scale_upto + next * 2) * 4 * sizeof(ccv_dense_matrix_t*)); - if (params.size.height != _cascade[0]->size.height || params.size.width != _cascade[0]->size.width) - ccv_resample(a, &pyr[0], 0, a->rows * _cascade[0]->size.height / params.size.height, a->cols * _cascade[0]->size.width / params.size.width, CCV_INTER_AREA); - else - pyr[0] = a; - int i, j, k, t, x, y, q; - for (i = 1; i < ccv_min(params.interval + 1, scale_upto + next * 2); i++) - ccv_resample(pyr[0], &pyr[i * 4], 0, (int)(pyr[0]->rows / pow(scale, i)), (int)(pyr[0]->cols / pow(scale, i)), CCV_INTER_AREA); - for (i = next; i < scale_upto + next * 2; i++) - ccv_sample_down(pyr[i * 4 - next * 4], &pyr[i * 4], 0, 0, 0); - if (params.accurate) - for (i = next * 2; i < scale_upto + next * 2; i++) - { - ccv_sample_down(pyr[i * 4 - next * 4], &pyr[i * 4 + 1], 0, 1, 0); - ccv_sample_down(pyr[i * 4 - next * 4], &pyr[i * 4 + 2], 0, 0, 1); - ccv_sample_down(pyr[i * 4 - next * 4], &pyr[i * 4 + 3], 0, 1, 1); - } - ccv_array_t* idx_seq; - ccv_array_t* seq = ccv_array_new(sizeof(ccv_comp_t), 64, 0); - ccv_array_t* seq2 = ccv_array_new(sizeof(ccv_comp_t), 64, 0); - ccv_array_t* result_seq = ccv_array_new(sizeof(ccv_comp_t), 64, 0); - /* detect in multi scale */ - for (t = 0; t < count; t++) - { - ccv_bbf_classifier_cascade_t* cascade = _cascade[t]; - float scale_x = (float) params.size.width / (float) cascade->size.width; - float scale_y = (float) params.size.height / (float) cascade->size.height; - ccv_array_clear(seq); - for (i = 0; i < scale_upto; i++) - { - int dx[] = {0, 1, 0, 1}; - int dy[] = {0, 0, 1, 1}; - int i_rows = pyr[i * 4 + next * 8]->rows - (cascade->size.height >> 2); - int steps[] = { pyr[i * 4]->step, pyr[i * 4 + next * 4]->step, pyr[i * 4 + next * 8]->step }; - int i_cols = pyr[i * 4 + next * 8]->cols - (cascade->size.width >> 2); - int paddings[] = { pyr[i * 4]->step * 4 - i_cols * 4, - pyr[i * 4 + next * 4]->step * 2 - i_cols * 2, - pyr[i * 4 + next * 8]->step - i_cols }; - for (q = 0; q < (params.accurate ? 4 : 1); q++) - { - unsigned char* u8[] = { pyr[i * 4]->data.u8 + dx[q] * 2 + dy[q] * pyr[i * 4]->step * 2, pyr[i * 4 + next * 4]->data.u8 + dx[q] + dy[q] * pyr[i * 4 + next * 4]->step, pyr[i * 4 + next * 8 + q]->data.u8 }; - for (y = 0; y < i_rows; y++) - { - for (x = 0; x < i_cols; x++) - { - float sum; - int flag = 1; - ccv_bbf_stage_classifier_t* classifier = cascade->stage_classifier; - for (j = 0; j < cascade->count; ++j, ++classifier) - { - sum = 0; - float* alpha = classifier->alpha; - ccv_bbf_feature_t* feature = classifier->feature; - for (k = 0; k < classifier->count; ++k, alpha += 2, ++feature) - sum += alpha[_ccv_run_bbf_feature(feature, steps, u8)]; - if (sum < classifier->threshold) - { - flag = 0; - break; - } - } - if (flag) - { - ccv_comp_t comp; - comp.rect = ccv_rect((int)((x * 4 + dx[q] * 2) * scale_x + 0.5), (int)((y * 4 + dy[q] * 2) * scale_y + 0.5), (int)(cascade->size.width * scale_x + 0.5), (int)(cascade->size.height * scale_y + 0.5)); - comp.neighbors = 1; - comp.classification.id = t; - comp.classification.confidence = sum; - ccv_array_push(seq, &comp); - } - u8[0] += 4; - u8[1] += 2; - u8[2] += 1; - } - u8[0] += paddings[0]; - u8[1] += paddings[1]; - u8[2] += paddings[2]; - } - } - scale_x *= scale; - scale_y *= scale; - } - - /* the following code from OpenCV's haar feature implementation */ - if(params.min_neighbors == 0) - { - for (i = 0; i < seq->rnum; i++) - { - ccv_comp_t* comp = (ccv_comp_t*)ccv_array_get(seq, i); - ccv_array_push(result_seq, comp); - } - } else { - idx_seq = 0; - ccv_array_clear(seq2); - // group retrieved rectangles in order to filter out noise - int ncomp = ccv_array_group(seq, &idx_seq, _ccv_is_equal_same_class, 0); - ccv_comp_t* comps = (ccv_comp_t*)ccmalloc((ncomp + 1) * sizeof(ccv_comp_t)); - memset(comps, 0, (ncomp + 1) * sizeof(ccv_comp_t)); - - // count number of neighbors - for(i = 0; i < seq->rnum; i++) - { - ccv_comp_t r1 = *(ccv_comp_t*)ccv_array_get(seq, i); - int idx = *(int*)ccv_array_get(idx_seq, i); - - if (comps[idx].neighbors == 0) - comps[idx].classification.confidence = r1.classification.confidence; - - ++comps[idx].neighbors; - - comps[idx].rect.x += r1.rect.x; - comps[idx].rect.y += r1.rect.y; - comps[idx].rect.width += r1.rect.width; - comps[idx].rect.height += r1.rect.height; - comps[idx].classification.id = r1.classification.id; - comps[idx].classification.confidence = ccv_max(comps[idx].classification.confidence, r1.classification.confidence); - } - - // calculate average bounding box - for(i = 0; i < ncomp; i++) - { - int n = comps[i].neighbors; - if(n >= params.min_neighbors) - { - ccv_comp_t comp; - comp.rect.x = (comps[i].rect.x * 2 + n) / (2 * n); - comp.rect.y = (comps[i].rect.y * 2 + n) / (2 * n); - comp.rect.width = (comps[i].rect.width * 2 + n) / (2 * n); - comp.rect.height = (comps[i].rect.height * 2 + n) / (2 * n); - comp.neighbors = comps[i].neighbors; - comp.classification.id = comps[i].classification.id; - comp.classification.confidence = comps[i].classification.confidence; - ccv_array_push(seq2, &comp); - } - } - - // filter out small face rectangles inside large face rectangles - for(i = 0; i < seq2->rnum; i++) - { - ccv_comp_t r1 = *(ccv_comp_t*)ccv_array_get(seq2, i); - int flag = 1; - - for(j = 0; j < seq2->rnum; j++) - { - ccv_comp_t r2 = *(ccv_comp_t*)ccv_array_get(seq2, j); - int distance = (int)(r2.rect.width * 0.25 + 0.5); - - if(i != j && - r1.classification.id == r2.classification.id && - r1.rect.x >= r2.rect.x - distance && - r1.rect.y >= r2.rect.y - distance && - r1.rect.x + r1.rect.width <= r2.rect.x + r2.rect.width + distance && - r1.rect.y + r1.rect.height <= r2.rect.y + r2.rect.height + distance && - (r2.neighbors > ccv_max(3, r1.neighbors) || r1.neighbors < 3)) - { - flag = 0; - break; - } - } - - if(flag) - ccv_array_push(result_seq, &r1); - } - ccv_array_free(idx_seq); - ccfree(comps); - } - } - - ccv_array_free(seq); - ccv_array_free(seq2); - - ccv_array_t* result_seq2; - /* the following code from OpenCV's haar feature implementation */ - if (params.flags & CCV_BBF_NO_NESTED) - { - result_seq2 = ccv_array_new(sizeof(ccv_comp_t), 64, 0); - idx_seq = 0; - // group retrieved rectangles in order to filter out noise - int ncomp = ccv_array_group(result_seq, &idx_seq, _ccv_is_equal, 0); - ccv_comp_t* comps = (ccv_comp_t*)ccmalloc((ncomp + 1) * sizeof(ccv_comp_t)); - memset(comps, 0, (ncomp + 1) * sizeof(ccv_comp_t)); - - // count number of neighbors - for(i = 0; i < result_seq->rnum; i++) - { - ccv_comp_t r1 = *(ccv_comp_t*)ccv_array_get(result_seq, i); - int idx = *(int*)ccv_array_get(idx_seq, i); - - if (comps[idx].neighbors == 0 || comps[idx].classification.confidence < r1.classification.confidence) - { - comps[idx].classification.confidence = r1.classification.confidence; - comps[idx].neighbors = 1; - comps[idx].rect = r1.rect; - comps[idx].classification.id = r1.classification.id; - } - } - - // calculate average bounding box - for(i = 0; i < ncomp; i++) - if(comps[i].neighbors) - ccv_array_push(result_seq2, &comps[i]); - - ccv_array_free(result_seq); - ccfree(comps); - } else { - result_seq2 = result_seq; - } - - for (i = 1; i < scale_upto + next * 2; i++) - ccv_matrix_free(pyr[i * 4]); - if (params.accurate) - for (i = next * 2; i < scale_upto + next * 2; i++) - { - ccv_matrix_free(pyr[i * 4 + 1]); - ccv_matrix_free(pyr[i * 4 + 2]); - ccv_matrix_free(pyr[i * 4 + 3]); - } - if (params.size.height != _cascade[0]->size.height || params.size.width != _cascade[0]->size.width) - ccv_matrix_free(pyr[0]); - - return result_seq2; -} - -ccv_bbf_classifier_cascade_t* ccv_bbf_read_classifier_cascade(const char* directory) -{ - char buf[1024]; - sprintf(buf, "%s/cascade.txt", directory); - int s, i; - FILE* r = fopen(buf, "r"); - if (r == 0) - return 0; - ccv_bbf_classifier_cascade_t* cascade = (ccv_bbf_classifier_cascade_t*)ccmalloc(sizeof(ccv_bbf_classifier_cascade_t)); - s = fscanf(r, "%d %d %d", &cascade->count, &cascade->size.width, &cascade->size.height); - assert(s > 0); - cascade->stage_classifier = (ccv_bbf_stage_classifier_t*)ccmalloc(cascade->count * sizeof(ccv_bbf_stage_classifier_t)); - for (i = 0; i < cascade->count; i++) - { - sprintf(buf, "%s/stage-%d.txt", directory, i); - if (_ccv_read_bbf_stage_classifier(buf, &cascade->stage_classifier[i]) < 0) - { - cascade->count = i; - break; - } - } - fclose(r); - return cascade; -} - -ccv_bbf_classifier_cascade_t* ccv_bbf_classifier_cascade_read_binary(char* s) -{ - int i; - ccv_bbf_classifier_cascade_t* cascade = (ccv_bbf_classifier_cascade_t*)ccmalloc(sizeof(ccv_bbf_classifier_cascade_t)); - memcpy(&cascade->count, s, sizeof(cascade->count)); s += sizeof(cascade->count); - memcpy(&cascade->size.width, s, sizeof(cascade->size.width)); s += sizeof(cascade->size.width); - memcpy(&cascade->size.height, s, sizeof(cascade->size.height)); s += sizeof(cascade->size.height); - ccv_bbf_stage_classifier_t* classifier = cascade->stage_classifier = (ccv_bbf_stage_classifier_t*)ccmalloc(cascade->count * sizeof(ccv_bbf_stage_classifier_t)); - for (i = 0; i < cascade->count; i++, classifier++) - { - memcpy(&classifier->count, s, sizeof(classifier->count)); s += sizeof(classifier->count); - memcpy(&classifier->threshold, s, sizeof(classifier->threshold)); s += sizeof(classifier->threshold); - classifier->feature = (ccv_bbf_feature_t*)ccmalloc(classifier->count * sizeof(ccv_bbf_feature_t)); - classifier->alpha = (float*)ccmalloc(classifier->count * 2 * sizeof(float)); - memcpy(classifier->feature, s, classifier->count * sizeof(ccv_bbf_feature_t)); s += classifier->count * sizeof(ccv_bbf_feature_t); - memcpy(classifier->alpha, s, classifier->count * 2 * sizeof(float)); s += classifier->count * 2 * sizeof(float); - } - return cascade; - -} - -int ccv_bbf_classifier_cascade_write_binary(ccv_bbf_classifier_cascade_t* cascade, char* s, int slen) -{ - int i; - int len = sizeof(cascade->count) + sizeof(cascade->size.width) + sizeof(cascade->size.height); - ccv_bbf_stage_classifier_t* classifier = cascade->stage_classifier; - for (i = 0; i < cascade->count; i++, classifier++) - len += sizeof(classifier->count) + sizeof(classifier->threshold) + classifier->count * sizeof(ccv_bbf_feature_t) + classifier->count * 2 * sizeof(float); - if (slen >= len) - { - memcpy(s, &cascade->count, sizeof(cascade->count)); s += sizeof(cascade->count); - memcpy(s, &cascade->size.width, sizeof(cascade->size.width)); s += sizeof(cascade->size.width); - memcpy(s, &cascade->size.height, sizeof(cascade->size.height)); s += sizeof(cascade->size.height); - classifier = cascade->stage_classifier; - for (i = 0; i < cascade->count; i++, classifier++) - { - memcpy(s, &classifier->count, sizeof(classifier->count)); s += sizeof(classifier->count); - memcpy(s, &classifier->threshold, sizeof(classifier->threshold)); s += sizeof(classifier->threshold); - memcpy(s, classifier->feature, classifier->count * sizeof(ccv_bbf_feature_t)); s += classifier->count * sizeof(ccv_bbf_feature_t); - memcpy(s, classifier->alpha, classifier->count * 2 * sizeof(float)); s += classifier->count * 2 * sizeof(float); - } - } - return len; -} - -void ccv_bbf_classifier_cascade_free(ccv_bbf_classifier_cascade_t* cascade) -{ - int i; - for (i = 0; i < cascade->count; ++i) - { - ccfree(cascade->stage_classifier[i].feature); - ccfree(cascade->stage_classifier[i].alpha); - } - ccfree(cascade->stage_classifier); - ccfree(cascade); -} \ No newline at end of file +#ifdef HAVE_GSL \ No newline at end of file diff --git a/test/source.cc b/test/source.cc index f0094b3..644923c 100644 --- a/test/source.cc +++ b/test/source.cc @@ -58,1516 +58,3 @@ static inline int _ccv_run_bbf_feature(ccv_bbf_feature_t *feature, int *step, un #undef nf_at return 1; } - -static int _ccv_read_bbf_stage_classifier(const char *file, ccv_bbf_stage_classifier_t *classifier) -{ - FILE *r = fopen(file, "r"); - if (r == 0) - return -1; - int stat = 0; - stat |= fscanf(r, "%d", &classifier->count); - union { - float fl; - int i; - } fli; - stat |= fscanf(r, "%d", &fli.i); - classifier->threshold = fli.fl; - classifier->feature = (ccv_bbf_feature_t *)ccmalloc(classifier->count * sizeof(ccv_bbf_feature_t)); - classifier->alpha = (float *)ccmalloc(classifier->count * 2 * sizeof(float)); - int i, j; - for (i = 0; i < classifier->count; i++) - { - stat |= fscanf(r, "%d", &classifier->feature[i].size); - for (j = 0; j < classifier->feature[i].size; j++) - { - stat |= fscanf(r, "%d %d %d", &classifier->feature[i].px[j], &classifier->feature[i].py[j], &classifier->feature[i].pz[j]); - stat |= fscanf(r, "%d %d %d", &classifier->feature[i].nx[j], &classifier->feature[i].ny[j], &classifier->feature[i].nz[j]); - } - union { - float fl; - int i; - } flia, flib; - stat |= fscanf(r, "%d %d", &flia.i, &flib.i); - classifier->alpha[i * 2] = flia.fl; - classifier->alpha[i * 2 + 1] = flib.fl; - } - fclose(r); - return 0; -} - -#ifdef HAVE_GSL - -static unsigned int _ccv_bbf_time_measure() -{ - struct timeval tv; - gettimeofday(&tv, 0); - return tv.tv_sec * 1000000 + tv.tv_usec; -} - -#define less_than(a, b, aux) ((a) < (b)) -CCV_IMPLEMENT_QSORT(_ccv_sort_32f, float, less_than) -#undef less_than - -static void _ccv_bbf_eval_data(ccv_bbf_stage_classifier_t *classifier, unsigned char **posdata, int posnum, unsigned char **negdata, int negnum, ccv_size_t size, float *peval, float *neval) -{ - int i, j; - int steps[] = {_ccv_width_padding(size.width), - _ccv_width_padding(size.width >> 1), - _ccv_width_padding(size.width >> 2)}; - int isizs0 = steps[0] * size.height; - int isizs01 = isizs0 + steps[1] * (size.height >> 1); - for (i = 0; i < posnum; i++) - { - unsigned char *u8[] = {posdata[i], posdata[i] + isizs0, posdata[i] + isizs01}; - float sum = 0; - float *alpha = classifier->alpha; - ccv_bbf_feature_t *feature = classifier->feature; - for (j = 0; j < classifier->count; ++j, alpha += 2, ++feature) - sum += alpha[_ccv_run_bbf_feature(feature, steps, u8)]; - peval[i] = sum; - } - for (i = 0; i < negnum; i++) - { - unsigned char *u8[] = {negdata[i], negdata[i] + isizs0, negdata[i] + isizs01}; - float sum = 0; - float *alpha = classifier->alpha; - ccv_bbf_feature_t *feature = classifier->feature; - for (j = 0; j < classifier->count; ++j, alpha += 2, ++feature) - sum += alpha[_ccv_run_bbf_feature(feature, steps, u8)]; - neval[i] = sum; - } -} - -static int _ccv_prune_positive_data(ccv_bbf_classifier_cascade_t *cascade, unsigned char **posdata, int posnum, ccv_size_t size) -{ - float *peval = (float *)ccmalloc(posnum * sizeof(float)); - int i, j, k, rpos = posnum; - for (i = 0; i < cascade->count; i++) - { - _ccv_bbf_eval_data(cascade->stage_classifier + i, posdata, rpos, 0, 0, size, peval, 0); - k = 0; - for (j = 0; j < rpos; j++) - if (peval[j] >= cascade->stage_classifier[i].threshold) - { - posdata[k] = posdata[j]; - ++k; - } - else - { - ccfree(posdata[j]); - } - rpos = k; - } - ccfree(peval); - return rpos; -} - -static int _ccv_prepare_background_data(ccv_bbf_classifier_cascade_t *cascade, char **bgfiles, int bgnum, unsigned char **negdata, int negnum) -{ - int t, i, j, k, q; - int negperbg; - int negtotal = 0; - int steps[] = {_ccv_width_padding(cascade->size.width), - _ccv_width_padding(cascade->size.width >> 1), - _ccv_width_padding(cascade->size.width >> 2)}; - int isizs0 = steps[0] * cascade->size.height; - int isizs1 = steps[1] * (cascade->size.height >> 1); - int isizs2 = steps[2] * (cascade->size.height >> 2); - int *idcheck = (int *)ccmalloc(negnum * sizeof(int)); - - gsl_rng_env_setup(); - - gsl_rng *rng = gsl_rng_alloc(gsl_rng_default); - gsl_rng_set(rng, (unsigned long int)idcheck); - - ccv_size_t imgsz = cascade->size; - int rneg = negtotal; - for (t = 0; negtotal < negnum; t++) - { - PRINT(CCV_CLI_INFO, "preparing negative data ... 0%%"); - for (i = 0; i < bgnum; i++) - { - negperbg = (t < 2) ? (negnum - negtotal) / (bgnum - i) + 1 : negnum - negtotal; - ccv_dense_matrix_t *image = 0; - ccv_read(bgfiles[i], &image, CCV_IO_GRAY | CCV_IO_ANY_FILE); - assert((image->type & CCV_C1) && (image->type & CCV_8U)); - if (image == 0) - { - PRINT(CCV_CLI_ERROR, "\n%s file corrupted\n", bgfiles[i]); - continue; - } - if (t % 2 != 0) - ccv_flip(image, 0, 0, CCV_FLIP_X); - if (t % 4 >= 2) - ccv_flip(image, 0, 0, CCV_FLIP_Y); - ccv_bbf_param_t params = {.interval = 3, .min_neighbors = 0, .accurate = 1, .flags = 0, .size = cascade->size}; - ccv_array_t *detected = ccv_bbf_detect_objects(image, &cascade, 1, params); - memset(idcheck, 0, ccv_min(detected->rnum, negperbg) * sizeof(int)); - for (j = 0; j < ccv_min(detected->rnum, negperbg); j++) - { - int r = gsl_rng_uniform_int(rng, detected->rnum); - int flag = 1; - ccv_rect_t *rect = (ccv_rect_t *)ccv_array_get(detected, r); - while (flag) - { - flag = 0; - for (k = 0; k < j; k++) - if (r == idcheck[k]) - { - flag = 1; - r = gsl_rng_uniform_int(rng, detected->rnum); - break; - } - rect = (ccv_rect_t *)ccv_array_get(detected, r); - if ((rect->x < 0) || (rect->y < 0) || (rect->width + rect->x > image->cols) || (rect->height + rect->y > image->rows)) - { - flag = 1; - r = gsl_rng_uniform_int(rng, detected->rnum); - } - } - idcheck[j] = r; - ccv_dense_matrix_t *temp = 0; - ccv_dense_matrix_t *imgs0 = 0; - ccv_dense_matrix_t *imgs1 = 0; - ccv_dense_matrix_t *imgs2 = 0; - ccv_slice(image, (ccv_matrix_t **)&temp, 0, rect->y, rect->x, rect->height, rect->width); - ccv_resample(temp, &imgs0, 0, imgsz.height, imgsz.width, CCV_INTER_AREA); - assert(imgs0->step == steps[0]); - ccv_matrix_free(temp); - ccv_sample_down(imgs0, &imgs1, 0, 0, 0); - assert(imgs1->step == steps[1]); - ccv_sample_down(imgs1, &imgs2, 0, 0, 0); - assert(imgs2->step == steps[2]); - - negdata[negtotal] = (unsigned char *)ccmalloc(isizs0 + isizs1 + isizs2); - unsigned char *u8s0 = negdata[negtotal]; - unsigned char *u8s1 = negdata[negtotal] + isizs0; - unsigned char *u8s2 = negdata[negtotal] + isizs0 + isizs1; - unsigned char *u8[] = {u8s0, u8s1, u8s2}; - memcpy(u8s0, imgs0->data.u8, imgs0->rows * imgs0->step); - ccv_matrix_free(imgs0); - memcpy(u8s1, imgs1->data.u8, imgs1->rows * imgs1->step); - ccv_matrix_free(imgs1); - memcpy(u8s2, imgs2->data.u8, imgs2->rows * imgs2->step); - ccv_matrix_free(imgs2); - - flag = 1; - ccv_bbf_stage_classifier_t *classifier = cascade->stage_classifier; - for (k = 0; k < cascade->count; ++k, ++classifier) - { - float sum = 0; - float *alpha = classifier->alpha; - ccv_bbf_feature_t *feature = classifier->feature; - for (q = 0; q < classifier->count; ++q, alpha += 2, ++feature) - sum += alpha[_ccv_run_bbf_feature(feature, steps, u8)]; - if (sum < classifier->threshold) - { - flag = 0; - break; - } - } - if (!flag) - ccfree(negdata[negtotal]); - else - { - ++negtotal; - if (negtotal >= negnum) - break; - } - } - ccv_array_free(detected); - ccv_matrix_free(image); - ccv_drain_cache(); - PRINT(CCV_CLI_INFO, "\rpreparing negative data ... %2d%%", 100 * negtotal / negnum); - fflush(0); - if (negtotal >= negnum) - break; - } - if (rneg == negtotal) - break; - rneg = negtotal; - PRINT(CCV_CLI_INFO, "\nentering additional round %d\n", t + 1); - } - gsl_rng_free(rng); - ccfree(idcheck); - ccv_drain_cache(); - PRINT(CCV_CLI_INFO, "\n"); - return negtotal; -} - -static void _ccv_prepare_positive_data(ccv_dense_matrix_t **posimg, unsigned char **posdata, ccv_size_t size, int posnum) -{ - PRINT(CCV_CLI_INFO, "preparing positive data ... 0%%"); - int i; - for (i = 0; i < posnum; i++) - { - ccv_dense_matrix_t *imgs0 = posimg[i]; - ccv_dense_matrix_t *imgs1 = 0; - ccv_dense_matrix_t *imgs2 = 0; - assert((imgs0->type & CCV_C1) && (imgs0->type & CCV_8U) && imgs0->rows == size.height && imgs0->cols == size.width); - ccv_sample_down(imgs0, &imgs1, 0, 0, 0); - ccv_sample_down(imgs1, &imgs2, 0, 0, 0); - int isizs0 = imgs0->rows * imgs0->step; - int isizs1 = imgs1->rows * imgs1->step; - int isizs2 = imgs2->rows * imgs2->step; - - posdata[i] = (unsigned char *)ccmalloc(isizs0 + isizs1 + isizs2); - memcpy(posdata[i], imgs0->data.u8, isizs0); - memcpy(posdata[i] + isizs0, imgs1->data.u8, isizs1); - memcpy(posdata[i] + isizs0 + isizs1, imgs2->data.u8, isizs2); - - PRINT(CCV_CLI_INFO, "\rpreparing positive data ... %2d%%", 100 * (i + 1) / posnum); - fflush(0); - - ccv_matrix_free(imgs1); - ccv_matrix_free(imgs2); - } - ccv_drain_cache(); - PRINT(CCV_CLI_INFO, "\n"); -} - -typedef struct -{ - double fitness; - int pk, nk; - int age; - double error; - ccv_bbf_feature_t feature; -} ccv_bbf_gene_t; - -static inline void _ccv_bbf_genetic_fitness(ccv_bbf_gene_t *gene) -{ - gene->fitness = (1 - gene->error) * exp(-0.01 * gene->age) * exp((gene->pk + gene->nk) * log(1.015)); -} - -static inline int _ccv_bbf_exist_gene_feature(ccv_bbf_gene_t *gene, int x, int y, int z) -{ - int i; - for (i = 0; i < gene->pk; i++) - if (z == gene->feature.pz[i] && x == gene->feature.px[i] && y == gene->feature.py[i]) - return 1; - for (i = 0; i < gene->nk; i++) - if (z == gene->feature.nz[i] && x == gene->feature.nx[i] && y == gene->feature.ny[i]) - return 1; - return 0; -} - -static inline void _ccv_bbf_randomize_gene(gsl_rng *rng, ccv_bbf_gene_t *gene, int *rows, int *cols) -{ - int i; - do - { - gene->pk = gsl_rng_uniform_int(rng, CCV_BBF_POINT_MAX - 1) + 1; - gene->nk = gsl_rng_uniform_int(rng, CCV_BBF_POINT_MAX - 1) + 1; - } while (gene->pk + gene->nk < CCV_BBF_POINT_MIN); /* a hard restriction of at least 3 points have to be examed */ - gene->feature.size = ccv_max(gene->pk, gene->nk); - gene->age = 0; - for (i = 0; i < CCV_BBF_POINT_MAX; i++) - { - gene->feature.pz[i] = -1; - gene->feature.nz[i] = -1; - } - int x, y, z; - for (i = 0; i < gene->pk; i++) - { - do - { - z = gsl_rng_uniform_int(rng, 3); - x = gsl_rng_uniform_int(rng, cols[z]); - y = gsl_rng_uniform_int(rng, rows[z]); - } while (_ccv_bbf_exist_gene_feature(gene, x, y, z)); - gene->feature.pz[i] = z; - gene->feature.px[i] = x; - gene->feature.py[i] = y; - } - for (i = 0; i < gene->nk; i++) - { - do - { - z = gsl_rng_uniform_int(rng, 3); - x = gsl_rng_uniform_int(rng, cols[z]); - y = gsl_rng_uniform_int(rng, rows[z]); - } while (_ccv_bbf_exist_gene_feature(gene, x, y, z)); - gene->feature.nz[i] = z; - gene->feature.nx[i] = x; - gene->feature.ny[i] = y; - } -} - -static inline double _ccv_bbf_error_rate(ccv_bbf_feature_t *feature, unsigned char **posdata, int posnum, unsigned char **negdata, int negnum, ccv_size_t size, double *pw, double *nw) -{ - int i; - int steps[] = {_ccv_width_padding(size.width), - _ccv_width_padding(size.width >> 1), - _ccv_width_padding(size.width >> 2)}; - int isizs0 = steps[0] * size.height; - int isizs01 = isizs0 + steps[1] * (size.height >> 1); - double error = 0; - for (i = 0; i < posnum; i++) - { - unsigned char *u8[] = {posdata[i], posdata[i] + isizs0, posdata[i] + isizs01}; - if (!_ccv_run_bbf_feature(feature, steps, u8)) - error += pw[i]; - } - for (i = 0; i < negnum; i++) - { - unsigned char *u8[] = {negdata[i], negdata[i] + isizs0, negdata[i] + isizs01}; - if (_ccv_run_bbf_feature(feature, steps, u8)) - error += nw[i]; - } - return error; -} - -#define less_than(fit1, fit2, aux) ((fit1).fitness >= (fit2).fitness) -static CCV_IMPLEMENT_QSORT(_ccv_bbf_genetic_qsort, ccv_bbf_gene_t, less_than) -#undef less_than - - static ccv_bbf_feature_t _ccv_bbf_genetic_optimize(unsigned char **posdata, int posnum, unsigned char **negdata, int negnum, int ftnum, ccv_size_t size, double *pw, double *nw) -{ - ccv_bbf_feature_t best; - /* seed (random method) */ - gsl_rng_env_setup(); - gsl_rng *rng = gsl_rng_alloc(gsl_rng_default); - union { - unsigned long int li; - double db; - } dbli; - dbli.db = pw[0] + nw[0]; - gsl_rng_set(rng, dbli.li); - int i, j; - int pnum = ftnum * 100; - assert(pnum > 0); - ccv_bbf_gene_t *gene = (ccv_bbf_gene_t *)ccmalloc(pnum * sizeof(ccv_bbf_gene_t)); - int rows[] = {size.height, size.height >> 1, size.height >> 2}; - int cols[] = {size.width, size.width >> 1, size.width >> 2}; - for (i = 0; i < pnum; i++) - _ccv_bbf_randomize_gene(rng, &gene[i], rows, cols); - unsigned int timer = _ccv_bbf_time_measure(); -#ifdef USE_OPENMP -#pragma omp parallel for private(i) schedule(dynamic) -#endif - for (i = 0; i < pnum; i++) - gene[i].error = _ccv_bbf_error_rate(&gene[i].feature, posdata, posnum, negdata, negnum, size, pw, nw); - timer = _ccv_bbf_time_measure() - timer; - for (i = 0; i < pnum; i++) - _ccv_bbf_genetic_fitness(&gene[i]); - double best_err = 1; - int rnum = ftnum * 39; /* number of randomize */ - int mnum = ftnum * 40; /* number of mutation */ - int hnum = ftnum * 20; /* number of hybrid */ - /* iteration stop crit : best no change in 40 iterations */ - int it = 0, t; - for (t = 0; it < 40; ++it, ++t) - { - int min_id = 0; - double min_err = gene[0].error; - for (i = 1; i < pnum; i++) - if (gene[i].error < min_err) - { - min_id = i; - min_err = gene[i].error; - } - min_err = gene[min_id].error = _ccv_bbf_error_rate(&gene[min_id].feature, posdata, posnum, negdata, negnum, size, pw, nw); - if (min_err < best_err) - { - best_err = min_err; - memcpy(&best, &gene[min_id].feature, sizeof(best)); - PRINT(CCV_CLI_INFO, "best bbf feature with error %f\n|-size: %d\n|-positive point: ", best_err, best.size); - for (i = 0; i < best.size; i++) - PRINT(CCV_CLI_INFO, "(%d %d %d), ", best.px[i], best.py[i], best.pz[i]); - PRINT(CCV_CLI_INFO, "\n|-negative point: "); - for (i = 0; i < best.size; i++) - PRINT(CCV_CLI_INFO, "(%d %d %d), ", best.nx[i], best.ny[i], best.nz[i]); - PRINT(CCV_CLI_INFO, "\n"); - it = 0; - } - PRINT(CCV_CLI_INFO, "minimum error achieved in round %d(%d) : %f with %d ms\n", t, it, min_err, timer / 1000); - _ccv_bbf_genetic_qsort(gene, pnum, 0); - for (i = 0; i < ftnum; i++) - ++gene[i].age; - for (i = ftnum; i < ftnum + mnum; i++) - { - int parent = gsl_rng_uniform_int(rng, ftnum); - memcpy(gene + i, gene + parent, sizeof(ccv_bbf_gene_t)); - /* three mutation strategy : 1. add, 2. remove, 3. refine */ - int pnm, pn = gsl_rng_uniform_int(rng, 2); - int *pnk[] = {&gene[i].pk, &gene[i].nk}; - int *pnx[] = {gene[i].feature.px, gene[i].feature.nx}; - int *pny[] = {gene[i].feature.py, gene[i].feature.ny}; - int *pnz[] = {gene[i].feature.pz, gene[i].feature.nz}; - int x, y, z; - int victim, decay = 1; - do - { - switch (gsl_rng_uniform_int(rng, 3)) - { - case 0: /* add */ - if (gene[i].pk == CCV_BBF_POINT_MAX && gene[i].nk == CCV_BBF_POINT_MAX) - break; - while (*pnk[pn] + 1 > CCV_BBF_POINT_MAX) - pn = gsl_rng_uniform_int(rng, 2); - do - { - z = gsl_rng_uniform_int(rng, 3); - x = gsl_rng_uniform_int(rng, cols[z]); - y = gsl_rng_uniform_int(rng, rows[z]); - } while (_ccv_bbf_exist_gene_feature(&gene[i], x, y, z)); - pnz[pn][*pnk[pn]] = z; - pnx[pn][*pnk[pn]] = x; - pny[pn][*pnk[pn]] = y; - ++(*pnk[pn]); - gene[i].feature.size = ccv_max(gene[i].pk, gene[i].nk); - decay = gene[i].age = 0; - break; - case 1: /* remove */ - if (gene[i].pk + gene[i].nk <= CCV_BBF_POINT_MIN) /* at least 3 points have to be examed */ - break; - while (*pnk[pn] - 1 <= 0) // || *pnk[pn] + *pnk[!pn] - 1 < CCV_BBF_POINT_MIN) - pn = gsl_rng_uniform_int(rng, 2); - victim = gsl_rng_uniform_int(rng, *pnk[pn]); - for (j = victim; j < *pnk[pn] - 1; j++) - { - pnz[pn][j] = pnz[pn][j + 1]; - pnx[pn][j] = pnx[pn][j + 1]; - pny[pn][j] = pny[pn][j + 1]; - } - pnz[pn][*pnk[pn] - 1] = -1; - --(*pnk[pn]); - gene[i].feature.size = ccv_max(gene[i].pk, gene[i].nk); - decay = gene[i].age = 0; - break; - case 2: /* refine */ - pnm = gsl_rng_uniform_int(rng, *pnk[pn]); - do - { - z = gsl_rng_uniform_int(rng, 3); - x = gsl_rng_uniform_int(rng, cols[z]); - y = gsl_rng_uniform_int(rng, rows[z]); - } while (_ccv_bbf_exist_gene_feature(&gene[i], x, y, z)); - pnz[pn][pnm] = z; - pnx[pn][pnm] = x; - pny[pn][pnm] = y; - decay = gene[i].age = 0; - break; - } - } while (decay); - } - for (i = ftnum + mnum; i < ftnum + mnum + hnum; i++) - { - /* hybrid strategy: taking positive points from dad, negative points from mum */ - int dad, mum; - do - { - dad = gsl_rng_uniform_int(rng, ftnum); - mum = gsl_rng_uniform_int(rng, ftnum); - } while (dad == mum || gene[dad].pk + gene[mum].nk < CCV_BBF_POINT_MIN); /* at least 3 points have to be examed */ - for (j = 0; j < CCV_BBF_POINT_MAX; j++) - { - gene[i].feature.pz[j] = -1; - gene[i].feature.nz[j] = -1; - } - gene[i].pk = gene[dad].pk; - for (j = 0; j < gene[i].pk; j++) - { - gene[i].feature.pz[j] = gene[dad].feature.pz[j]; - gene[i].feature.px[j] = gene[dad].feature.px[j]; - gene[i].feature.py[j] = gene[dad].feature.py[j]; - } - gene[i].nk = gene[mum].nk; - for (j = 0; j < gene[i].nk; j++) - { - gene[i].feature.nz[j] = gene[mum].feature.nz[j]; - gene[i].feature.nx[j] = gene[mum].feature.nx[j]; - gene[i].feature.ny[j] = gene[mum].feature.ny[j]; - } - gene[i].feature.size = ccv_max(gene[i].pk, gene[i].nk); - gene[i].age = 0; - } - for (i = ftnum + mnum + hnum; i < ftnum + mnum + hnum + rnum; i++) - _ccv_bbf_randomize_gene(rng, &gene[i], rows, cols); - timer = _ccv_bbf_time_measure(); -#ifdef USE_OPENMP -#pragma omp parallel for private(i) schedule(dynamic) -#endif - for (i = 0; i < pnum; i++) - gene[i].error = _ccv_bbf_error_rate(&gene[i].feature, posdata, posnum, negdata, negnum, size, pw, nw); - timer = _ccv_bbf_time_measure() - timer; - for (i = 0; i < pnum; i++) - _ccv_bbf_genetic_fitness(&gene[i]); - } - ccfree(gene); - gsl_rng_free(rng); - return best; -} - -#define less_than(fit1, fit2, aux) ((fit1).error < (fit2).error) -static CCV_IMPLEMENT_QSORT(_ccv_bbf_best_qsort, ccv_bbf_gene_t, less_than) -#undef less_than - - static ccv_bbf_gene_t _ccv_bbf_best_gene(ccv_bbf_gene_t *gene, int pnum, int point_min, unsigned char **posdata, int posnum, unsigned char **negdata, int negnum, ccv_size_t size, double *pw, double *nw) -{ - int i; - unsigned int timer = _ccv_bbf_time_measure(); -#ifdef USE_OPENMP -#pragma omp parallel for private(i) schedule(dynamic) -#endif - for (i = 0; i < pnum; i++) - gene[i].error = _ccv_bbf_error_rate(&gene[i].feature, posdata, posnum, negdata, negnum, size, pw, nw); - timer = _ccv_bbf_time_measure() - timer; - _ccv_bbf_best_qsort(gene, pnum, 0); - int min_id = 0; - double min_err = gene[0].error; - for (i = 0; i < pnum; i++) - if (gene[i].nk + gene[i].pk >= point_min) - { - min_id = i; - min_err = gene[i].error; - break; - } - PRINT(CCV_CLI_INFO, "local best bbf feature with error %f\n|-size: %d\n|-positive point: ", min_err, gene[min_id].feature.size); - for (i = 0; i < gene[min_id].feature.size; i++) - PRINT(CCV_CLI_INFO, "(%d %d %d), ", gene[min_id].feature.px[i], gene[min_id].feature.py[i], gene[min_id].feature.pz[i]); - PRINT(CCV_CLI_INFO, "\n|-negative point: "); - for (i = 0; i < gene[min_id].feature.size; i++) - PRINT(CCV_CLI_INFO, "(%d %d %d), ", gene[min_id].feature.nx[i], gene[min_id].feature.ny[i], gene[min_id].feature.nz[i]); - PRINT(CCV_CLI_INFO, "\nthe computation takes %d ms\n", timer / 1000); - return gene[min_id]; -} - -static ccv_bbf_feature_t _ccv_bbf_convex_optimize(unsigned char **posdata, int posnum, unsigned char **negdata, int negnum, ccv_bbf_feature_t *best_feature, ccv_size_t size, double *pw, double *nw) -{ - ccv_bbf_gene_t best_gene; - /* seed (random method) */ - gsl_rng_env_setup(); - gsl_rng *rng = gsl_rng_alloc(gsl_rng_default); - union { - unsigned long int li; - double db; - } dbli; - dbli.db = pw[0] + nw[0]; - gsl_rng_set(rng, dbli.li); - int i, j, k, q, p, g, t; - int rows[] = {size.height, size.height >> 1, size.height >> 2}; - int cols[] = {size.width, size.width >> 1, size.width >> 2}; - int pnum = rows[0] * cols[0] + rows[1] * cols[1] + rows[2] * cols[2]; - ccv_bbf_gene_t *gene = (ccv_bbf_gene_t *)ccmalloc((pnum * (CCV_BBF_POINT_MAX * 2 + 1) * 2 + CCV_BBF_POINT_MAX * 2 + 1) * sizeof(ccv_bbf_gene_t)); - if (best_feature == 0) - { - /* bootstrapping the best feature, start from two pixels, one for positive, one for negative - * the bootstrapping process go like this: first, it will assign a random pixel as positive - * and enumerate every possible pixel as negative, and pick the best one. Then, enumerate every - * possible pixel as positive, and pick the best one, until it converges */ - memset(&best_gene, 0, sizeof(ccv_bbf_gene_t)); - for (i = 0; i < CCV_BBF_POINT_MAX; i++) - best_gene.feature.pz[i] = best_gene.feature.nz[i] = -1; - best_gene.pk = 1; - best_gene.nk = 0; - best_gene.feature.size = 1; - best_gene.feature.pz[0] = gsl_rng_uniform_int(rng, 3); - best_gene.feature.px[0] = gsl_rng_uniform_int(rng, cols[best_gene.feature.pz[0]]); - best_gene.feature.py[0] = gsl_rng_uniform_int(rng, rows[best_gene.feature.pz[0]]); - for (t = 0;; ++t) - { - g = 0; - if (t % 2 == 0) - { - for (i = 0; i < 3; i++) - for (j = 0; j < cols[i]; j++) - for (k = 0; k < rows[i]; k++) - if (i != best_gene.feature.pz[0] || j != best_gene.feature.px[0] || k != best_gene.feature.py[0]) - { - gene[g] = best_gene; - gene[g].pk = gene[g].nk = 1; - gene[g].feature.nz[0] = i; - gene[g].feature.nx[0] = j; - gene[g].feature.ny[0] = k; - g++; - } - } - else - { - for (i = 0; i < 3; i++) - for (j = 0; j < cols[i]; j++) - for (k = 0; k < rows[i]; k++) - if (i != best_gene.feature.nz[0] || j != best_gene.feature.nx[0] || k != best_gene.feature.ny[0]) - { - gene[g] = best_gene; - gene[g].pk = gene[g].nk = 1; - gene[g].feature.pz[0] = i; - gene[g].feature.px[0] = j; - gene[g].feature.py[0] = k; - g++; - } - } - PRINT(CCV_CLI_INFO, "bootstrapping round : %d\n", t); - ccv_bbf_gene_t local_gene = _ccv_bbf_best_gene(gene, g, 2, posdata, posnum, negdata, negnum, size, pw, nw); - if (local_gene.error >= best_gene.error - 1e-10) - break; - best_gene = local_gene; - } - } - else - { - best_gene.feature = *best_feature; - best_gene.pk = best_gene.nk = best_gene.feature.size; - for (i = 0; i < CCV_BBF_POINT_MAX; i++) - if (best_feature->pz[i] == -1) - { - best_gene.pk = i; - break; - } - for (i = 0; i < CCV_BBF_POINT_MAX; i++) - if (best_feature->nz[i] == -1) - { - best_gene.nk = i; - break; - } - } - /* after bootstrapping, the float search technique will do the following permutations: - * a). add a new point to positive or negative - * b). remove a point from positive or negative - * c). move an existing point in positive or negative to another position - * the three rules applied exhaustively, no heuristic used. */ - for (t = 0;; ++t) - { - g = 0; - for (i = 0; i < 3; i++) - for (j = 0; j < cols[i]; j++) - for (k = 0; k < rows[i]; k++) - if (!_ccv_bbf_exist_gene_feature(&best_gene, j, k, i)) - { - /* add positive point */ - if (best_gene.pk < CCV_BBF_POINT_MAX - 1) - { - gene[g] = best_gene; - gene[g].feature.pz[gene[g].pk] = i; - gene[g].feature.px[gene[g].pk] = j; - gene[g].feature.py[gene[g].pk] = k; - gene[g].pk++; - gene[g].feature.size = ccv_max(gene[g].pk, gene[g].nk); - g++; - } - /* add negative point */ - if (best_gene.nk < CCV_BBF_POINT_MAX - 1) - { - gene[g] = best_gene; - gene[g].feature.nz[gene[g].nk] = i; - gene[g].feature.nx[gene[g].nk] = j; - gene[g].feature.ny[gene[g].nk] = k; - gene[g].nk++; - gene[g].feature.size = ccv_max(gene[g].pk, gene[g].nk); - g++; - } - /* refine positive point */ - for (q = 0; q < best_gene.pk; q++) - { - gene[g] = best_gene; - gene[g].feature.pz[q] = i; - gene[g].feature.px[q] = j; - gene[g].feature.py[q] = k; - g++; - } - /* add positive point, remove negative point */ - if (best_gene.pk < CCV_BBF_POINT_MAX - 1 && best_gene.nk > 1) - { - for (q = 0; q < best_gene.nk; q++) - { - gene[g] = best_gene; - gene[g].feature.pz[gene[g].pk] = i; - gene[g].feature.px[gene[g].pk] = j; - gene[g].feature.py[gene[g].pk] = k; - gene[g].pk++; - for (p = q; p < best_gene.nk - 1; p++) - { - gene[g].feature.nz[p] = gene[g].feature.nz[p + 1]; - gene[g].feature.nx[p] = gene[g].feature.nx[p + 1]; - gene[g].feature.ny[p] = gene[g].feature.ny[p + 1]; - } - gene[g].feature.nz[gene[g].nk - 1] = -1; - gene[g].nk--; - gene[g].feature.size = ccv_max(gene[g].pk, gene[g].nk); - g++; - } - } - /* refine negative point */ - for (q = 0; q < best_gene.nk; q++) - { - gene[g] = best_gene; - gene[g].feature.nz[q] = i; - gene[g].feature.nx[q] = j; - gene[g].feature.ny[q] = k; - g++; - } - /* add negative point, remove positive point */ - if (best_gene.pk > 1 && best_gene.nk < CCV_BBF_POINT_MAX - 1) - { - for (q = 0; q < best_gene.pk; q++) - { - gene[g] = best_gene; - gene[g].feature.nz[gene[g].nk] = i; - gene[g].feature.nx[gene[g].nk] = j; - gene[g].feature.ny[gene[g].nk] = k; - gene[g].nk++; - for (p = q; p < best_gene.pk - 1; p++) - { - gene[g].feature.pz[p] = gene[g].feature.pz[p + 1]; - gene[g].feature.px[p] = gene[g].feature.px[p + 1]; - gene[g].feature.py[p] = gene[g].feature.py[p + 1]; - } - gene[g].feature.pz[gene[g].pk - 1] = -1; - gene[g].pk--; - gene[g].feature.size = ccv_max(gene[g].pk, gene[g].nk); - g++; - } - } - } - if (best_gene.pk > 1) - for (q = 0; q < best_gene.pk; q++) - { - gene[g] = best_gene; - for (i = q; i < best_gene.pk - 1; i++) - { - gene[g].feature.pz[i] = gene[g].feature.pz[i + 1]; - gene[g].feature.px[i] = gene[g].feature.px[i + 1]; - gene[g].feature.py[i] = gene[g].feature.py[i + 1]; - } - gene[g].feature.pz[gene[g].pk - 1] = -1; - gene[g].pk--; - gene[g].feature.size = ccv_max(gene[g].pk, gene[g].nk); - g++; - } - if (best_gene.nk > 1) - for (q = 0; q < best_gene.nk; q++) - { - gene[g] = best_gene; - for (i = q; i < best_gene.nk - 1; i++) - { - gene[g].feature.nz[i] = gene[g].feature.nz[i + 1]; - gene[g].feature.nx[i] = gene[g].feature.nx[i + 1]; - gene[g].feature.ny[i] = gene[g].feature.ny[i + 1]; - } - gene[g].feature.nz[gene[g].nk - 1] = -1; - gene[g].nk--; - gene[g].feature.size = ccv_max(gene[g].pk, gene[g].nk); - g++; - } - gene[g] = best_gene; - g++; - PRINT(CCV_CLI_INFO, "float search round : %d\n", t); - ccv_bbf_gene_t local_gene = _ccv_bbf_best_gene(gene, g, CCV_BBF_POINT_MIN, posdata, posnum, negdata, negnum, size, pw, nw); - if (local_gene.error >= best_gene.error - 1e-10) - break; - best_gene = local_gene; - } - ccfree(gene); - gsl_rng_free(rng); - return best_gene.feature; -} - -static int _ccv_write_bbf_stage_classifier(const char *file, ccv_bbf_stage_classifier_t *classifier) -{ - FILE *w = fopen(file, "wb"); - if (w == 0) - return -1; - fprintf(w, "%d\n", classifier->count); - union { - float fl; - int i; - } fli; - fli.fl = classifier->threshold; - fprintf(w, "%d\n", fli.i); - int i, j; - for (i = 0; i < classifier->count; i++) - { - fprintf(w, "%d\n", classifier->feature[i].size); - for (j = 0; j < classifier->feature[i].size; j++) - { - fprintf(w, "%d %d %d\n", classifier->feature[i].px[j], classifier->feature[i].py[j], classifier->feature[i].pz[j]); - fprintf(w, "%d %d %d\n", classifier->feature[i].nx[j], classifier->feature[i].ny[j], classifier->feature[i].nz[j]); - } - union { - float fl; - int i; - } flia, flib; - flia.fl = classifier->alpha[i * 2]; - flib.fl = classifier->alpha[i * 2 + 1]; - fprintf(w, "%d %d\n", flia.i, flib.i); - } - fclose(w); - return 0; -} - -static int _ccv_read_background_data(const char *file, unsigned char **negdata, int *negnum, ccv_size_t size) -{ - int stat = 0; - FILE *r = fopen(file, "rb"); - if (r == 0) - return -1; - stat |= fread(negnum, sizeof(int), 1, r); - int i; - int isizs012 = _ccv_width_padding(size.width) * size.height + - _ccv_width_padding(size.width >> 1) * (size.height >> 1) + - _ccv_width_padding(size.width >> 2) * (size.height >> 2); - for (i = 0; i < *negnum; i++) - { - negdata[i] = (unsigned char *)ccmalloc(isizs012); - stat |= fread(negdata[i], 1, isizs012, r); - } - fclose(r); - return 0; -} - -static int _ccv_write_background_data(const char *file, unsigned char **negdata, int negnum, ccv_size_t size) -{ - FILE *w = fopen(file, "w"); - if (w == 0) - return -1; - fwrite(&negnum, sizeof(int), 1, w); - int i; - int isizs012 = _ccv_width_padding(size.width) * size.height + - _ccv_width_padding(size.width >> 1) * (size.height >> 1) + - _ccv_width_padding(size.width >> 2) * (size.height >> 2); - for (i = 0; i < negnum; i++) - fwrite(negdata[i], 1, isizs012, w); - fclose(w); - return 0; -} - -static int _ccv_resume_bbf_cascade_training_state(const char *file, int *i, int *k, int *bg, double *pw, double *nw, int posnum, int negnum) -{ - int stat = 0; - FILE *r = fopen(file, "r"); - if (r == 0) - return -1; - stat |= fscanf(r, "%d %d %d", i, k, bg); - int j; - union { - double db; - int i[2]; - } dbi; - for (j = 0; j < posnum; j++) - { - stat |= fscanf(r, "%d %d", &dbi.i[0], &dbi.i[1]); - pw[j] = dbi.db; - } - for (j = 0; j < negnum; j++) - { - stat |= fscanf(r, "%d %d", &dbi.i[0], &dbi.i[1]); - nw[j] = dbi.db; - } - fclose(r); - return 0; -} - -static int _ccv_save_bbf_cacade_training_state(const char *file, int i, int k, int bg, double *pw, double *nw, int posnum, int negnum) -{ - FILE *w = fopen(file, "w"); - if (w == 0) - return -1; - fprintf(w, "%d %d %d\n", i, k, bg); - int j; - union { - double db; - int i[2]; - } dbi; - for (j = 0; j < posnum; ++j) - { - dbi.db = pw[j]; - fprintf(w, "%d %d ", dbi.i[0], dbi.i[1]); - } - fprintf(w, "\n"); - for (j = 0; j < negnum; ++j) - { - dbi.db = nw[j]; - fprintf(w, "%d %d ", dbi.i[0], dbi.i[1]); - } - fprintf(w, "\n"); - fclose(w); - return 0; -} - -void ccv_bbf_classifier_cascade_new(ccv_dense_matrix_t **posimg, int posnum, char **bgfiles, int bgnum, int negnum, ccv_size_t size, const char *dir, ccv_bbf_new_param_t params) -{ - int i, j, k; - /* allocate memory for usage */ - ccv_bbf_classifier_cascade_t *cascade = (ccv_bbf_classifier_cascade_t *)ccmalloc(sizeof(ccv_bbf_classifier_cascade_t)); - cascade->count = 0; - cascade->size = size; - cascade->stage_classifier = (ccv_bbf_stage_classifier_t *)ccmalloc(sizeof(ccv_bbf_stage_classifier_t)); - unsigned char **posdata = (unsigned char **)ccmalloc(posnum * sizeof(unsigned char *)); - unsigned char **negdata = (unsigned char **)ccmalloc(negnum * sizeof(unsigned char *)); - double *pw = (double *)ccmalloc(posnum * sizeof(double)); - double *nw = (double *)ccmalloc(negnum * sizeof(double)); - float *peval = (float *)ccmalloc(posnum * sizeof(float)); - float *neval = (float *)ccmalloc(negnum * sizeof(float)); - double inv_balance_k = 1. / params.balance_k; - /* balance factor k, and weighted with 0.01 */ - params.balance_k *= 0.01; - inv_balance_k *= 0.01; - - int steps[] = {_ccv_width_padding(cascade->size.width), - _ccv_width_padding(cascade->size.width >> 1), - _ccv_width_padding(cascade->size.width >> 2)}; - int isizs0 = steps[0] * cascade->size.height; - int isizs01 = isizs0 + steps[1] * (cascade->size.height >> 1); - - i = 0; - k = 0; - int bg = 0; - int cacheK = 10; - /* state resume code */ - char buf[1024]; - sprintf(buf, "%s/stat.txt", dir); - _ccv_resume_bbf_cascade_training_state(buf, &i, &k, &bg, pw, nw, posnum, negnum); - if (i > 0) - { - cascade->count = i; - ccfree(cascade->stage_classifier); - cascade->stage_classifier = (ccv_bbf_stage_classifier_t *)ccmalloc(i * sizeof(ccv_bbf_stage_classifier_t)); - for (j = 0; j < i; j++) - { - sprintf(buf, "%s/stage-%d.txt", dir, j); - _ccv_read_bbf_stage_classifier(buf, &cascade->stage_classifier[j]); - } - } - if (k > 0) - cacheK = k; - int rpos, rneg = 0; - if (bg) - { - sprintf(buf, "%s/negs.txt", dir); - _ccv_read_background_data(buf, negdata, &rneg, cascade->size); - } - - for (; i < params.layer; i++) - { - if (!bg) - { - rneg = _ccv_prepare_background_data(cascade, bgfiles, bgnum, negdata, negnum); - /* save state of background data */ - sprintf(buf, "%s/negs.txt", dir); - _ccv_write_background_data(buf, negdata, rneg, cascade->size); - bg = 1; - } - double totalw; - /* save state of cascade : level, weight etc. */ - sprintf(buf, "%s/stat.txt", dir); - _ccv_save_bbf_cacade_training_state(buf, i, k, bg, pw, nw, posnum, negnum); - ccv_bbf_stage_classifier_t classifier; - if (k > 0) - { - /* resume state of classifier */ - sprintf(buf, "%s/stage-%d.txt", dir, i); - _ccv_read_bbf_stage_classifier(buf, &classifier); - } - else - { - /* initialize classifier */ - for (j = 0; j < posnum; j++) - pw[j] = params.balance_k; - for (j = 0; j < rneg; j++) - nw[j] = inv_balance_k; - classifier.count = k; - classifier.threshold = 0; - classifier.feature = (ccv_bbf_feature_t *)ccmalloc(cacheK * sizeof(ccv_bbf_feature_t)); - classifier.alpha = (float *)ccmalloc(cacheK * 2 * sizeof(float)); - } - _ccv_prepare_positive_data(posimg, posdata, cascade->size, posnum); - rpos = _ccv_prune_positive_data(cascade, posdata, posnum, cascade->size); - PRINT(CCV_CLI_INFO, "%d postivie data and %d negative data in training\n", rpos, rneg); - /* reweight to 1.00 */ - totalw = 0; - for (j = 0; j < rpos; j++) - totalw += pw[j]; - for (j = 0; j < rneg; j++) - totalw += nw[j]; - for (j = 0; j < rpos; j++) - pw[j] = pw[j] / totalw; - for (j = 0; j < rneg; j++) - nw[j] = nw[j] / totalw; - for (;; k++) - { - /* get overall true-positive, false-positive rate and threshold */ - double tp = 0, fp = 0, etp = 0, efp = 0; - _ccv_bbf_eval_data(&classifier, posdata, rpos, negdata, rneg, cascade->size, peval, neval); - _ccv_sort_32f(peval, rpos, 0); - classifier.threshold = peval[(int)((1. - params.pos_crit) * rpos)] - 1e-6; - for (j = 0; j < rpos; j++) - { - if (peval[j] >= 0) - ++tp; - if (peval[j] >= classifier.threshold) - ++etp; - } - tp /= rpos; - etp /= rpos; - for (j = 0; j < rneg; j++) - { - if (neval[j] >= 0) - ++fp; - if (neval[j] >= classifier.threshold) - ++efp; - } - fp /= rneg; - efp /= rneg; - PRINT(CCV_CLI_INFO, "stage classifier real TP rate : %f, FP rate : %f\n", tp, fp); - PRINT(CCV_CLI_INFO, "stage classifier TP rate : %f, FP rate : %f at threshold : %f\n", etp, efp, classifier.threshold); - if (k > 0) - { - /* save classifier state */ - sprintf(buf, "%s/stage-%d.txt", dir, i); - _ccv_write_bbf_stage_classifier(buf, &classifier); - sprintf(buf, "%s/stat.txt", dir); - _ccv_save_bbf_cacade_training_state(buf, i, k, bg, pw, nw, posnum, negnum); - } - if (etp > params.pos_crit && efp < params.neg_crit) - break; - /* TODO: more post-process is needed in here */ - - /* select the best feature in current distribution through genetic algorithm optimization */ - ccv_bbf_feature_t best; - if (params.optimizer == CCV_BBF_GENETIC_OPT) - { - best = _ccv_bbf_genetic_optimize(posdata, rpos, negdata, rneg, params.feature_number, cascade->size, pw, nw); - } - else if (params.optimizer == CCV_BBF_FLOAT_OPT) - { - best = _ccv_bbf_convex_optimize(posdata, rpos, negdata, rneg, 0, cascade->size, pw, nw); - } - else - { - best = _ccv_bbf_genetic_optimize(posdata, rpos, negdata, rneg, params.feature_number, cascade->size, pw, nw); - best = _ccv_bbf_convex_optimize(posdata, rpos, negdata, rneg, &best, cascade->size, pw, nw); - } - double err = _ccv_bbf_error_rate(&best, posdata, rpos, negdata, rneg, cascade->size, pw, nw); - double rw = (1 - err) / err; - totalw = 0; - /* reweight */ - for (j = 0; j < rpos; j++) - { - unsigned char *u8[] = {posdata[j], posdata[j] + isizs0, posdata[j] + isizs01}; - if (!_ccv_run_bbf_feature(&best, steps, u8)) - pw[j] *= rw; - pw[j] *= params.balance_k; - totalw += pw[j]; - } - for (j = 0; j < rneg; j++) - { - unsigned char *u8[] = {negdata[j], negdata[j] + isizs0, negdata[j] + isizs01}; - if (_ccv_run_bbf_feature(&best, steps, u8)) - nw[j] *= rw; - nw[j] *= inv_balance_k; - totalw += nw[j]; - } - for (j = 0; j < rpos; j++) - pw[j] = pw[j] / totalw; - for (j = 0; j < rneg; j++) - nw[j] = nw[j] / totalw; - double c = log(rw); - PRINT(CCV_CLI_INFO, "coefficient of feature %d: %f\n", k + 1, c); - classifier.count = k + 1; - /* resizing classifier */ - if (k >= cacheK) - { - ccv_bbf_feature_t *feature = (ccv_bbf_feature_t *)ccmalloc(cacheK * 2 * sizeof(ccv_bbf_feature_t)); - memcpy(feature, classifier.feature, cacheK * sizeof(ccv_bbf_feature_t)); - ccfree(classifier.feature); - float *alpha = (float *)ccmalloc(cacheK * 4 * sizeof(float)); - memcpy(alpha, classifier.alpha, cacheK * 2 * sizeof(float)); - ccfree(classifier.alpha); - classifier.feature = feature; - classifier.alpha = alpha; - cacheK *= 2; - } - /* setup new feature */ - classifier.feature[k] = best; - classifier.alpha[k * 2] = -c; - classifier.alpha[k * 2 + 1] = c; - } - cascade->count = i + 1; - ccv_bbf_stage_classifier_t *stage_classifier = (ccv_bbf_stage_classifier_t *)ccmalloc(cascade->count * sizeof(ccv_bbf_stage_classifier_t)); - memcpy(stage_classifier, cascade->stage_classifier, i * sizeof(ccv_bbf_stage_classifier_t)); - ccfree(cascade->stage_classifier); - stage_classifier[i] = classifier; - cascade->stage_classifier = stage_classifier; - k = 0; - bg = 0; - for (j = 0; j < rpos; j++) - ccfree(posdata[j]); - for (j = 0; j < rneg; j++) - ccfree(negdata[j]); - } - - ccfree(neval); - ccfree(peval); - ccfree(nw); - ccfree(pw); - ccfree(negdata); - ccfree(posdata); - ccfree(cascade); -} -#else -void ccv_bbf_classifier_cascade_new(ccv_dense_matrix_t **posimg, int posnum, char **bgfiles, int bgnum, int negnum, ccv_size_t size, const char *dir, ccv_bbf_new_param_t params) -{ - fprintf(stderr, " ccv_bbf_classifier_cascade_new requires libgsl support, please compile ccv with libgsl.\n"); -} -#endif - -static int _ccv_is_equal(const void *_r1, const void *_r2, void *data) -{ - const ccv_comp_t *r1 = (const ccv_comp_t *)_r1; - const ccv_comp_t *r2 = (const ccv_comp_t *)_r2; - int distance = (int)(r1->rect.width * 0.25 + 0.5); - - return r2->rect.x <= r1->rect.x + distance && - r2->rect.x >= r1->rect.x - distance && - r2->rect.y <= r1->rect.y + distance && - r2->rect.y >= r1->rect.y - distance && - r2->rect.width <= (int)(r1->rect.width * 1.5 + 0.5) && - (int)(r2->rect.width * 1.5 + 0.5) >= r1->rect.width; -} - -static int _ccv_is_equal_same_class(const void *_r1, const void *_r2, void *data) -{ - const ccv_comp_t *r1 = (const ccv_comp_t *)_r1; - const ccv_comp_t *r2 = (const ccv_comp_t *)_r2; - int distance = (int)(r1->rect.width * 0.25 + 0.5); - - return r2->classification.id == r1->classification.id && - r2->rect.x <= r1->rect.x + distance && - r2->rect.x >= r1->rect.x - distance && - r2->rect.y <= r1->rect.y + distance && - r2->rect.y >= r1->rect.y - distance && - r2->rect.width <= (int)(r1->rect.width * 1.5 + 0.5) && - (int)(r2->rect.width * 1.5 + 0.5) >= r1->rect.width; -} - -ccv_array_t *ccv_bbf_detect_objects(ccv_dense_matrix_t *a, ccv_bbf_classifier_cascade_t **_cascade, int count, ccv_bbf_param_t params) -{ - int hr = a->rows / params.size.height; - int wr = a->cols / params.size.width; - double scale = pow(2., 1. / (params.interval + 1.)); - int next = params.interval + 1; - int scale_upto = (int)(log((double)ccv_min(hr, wr)) / log(scale)); - ccv_dense_matrix_t **pyr = (ccv_dense_matrix_t **)alloca((scale_upto + next * 2) * 4 * sizeof(ccv_dense_matrix_t *)); - memset(pyr, 0, (scale_upto + next * 2) * 4 * sizeof(ccv_dense_matrix_t *)); - if (params.size.height != _cascade[0]->size.height || params.size.width != _cascade[0]->size.width) - ccv_resample(a, &pyr[0], 0, a->rows * _cascade[0]->size.height / params.size.height, a->cols * _cascade[0]->size.width / params.size.width, CCV_INTER_AREA); - else - pyr[0] = a; - int i, j, k, t, x, y, q; - for (i = 1; i < ccv_min(params.interval + 1, scale_upto + next * 2); i++) - ccv_resample(pyr[0], &pyr[i * 4], 0, (int)(pyr[0]->rows / pow(scale, i)), (int)(pyr[0]->cols / pow(scale, i)), CCV_INTER_AREA); - for (i = next; i < scale_upto + next * 2; i++) - ccv_sample_down(pyr[i * 4 - next * 4], &pyr[i * 4], 0, 0, 0); - if (params.accurate) - for (i = next * 2; i < scale_upto + next * 2; i++) - { - ccv_sample_down(pyr[i * 4 - next * 4], &pyr[i * 4 + 1], 0, 1, 0); - ccv_sample_down(pyr[i * 4 - next * 4], &pyr[i * 4 + 2], 0, 0, 1); - ccv_sample_down(pyr[i * 4 - next * 4], &pyr[i * 4 + 3], 0, 1, 1); - } - ccv_array_t *idx_seq; - ccv_array_t *seq = ccv_array_new(sizeof(ccv_comp_t), 64, 0); - ccv_array_t *seq2 = ccv_array_new(sizeof(ccv_comp_t), 64, 0); - ccv_array_t *result_seq = ccv_array_new(sizeof(ccv_comp_t), 64, 0); - /* detect in multi scale */ - for (t = 0; t < count; t++) - { - ccv_bbf_classifier_cascade_t *cascade = _cascade[t]; - float scale_x = (float)params.size.width / (float)cascade->size.width; - float scale_y = (float)params.size.height / (float)cascade->size.height; - ccv_array_clear(seq); - for (i = 0; i < scale_upto; i++) - { - int dx[] = {0, 1, 0, 1}; - int dy[] = {0, 0, 1, 1}; - int i_rows = pyr[i * 4 + next * 8]->rows - (cascade->size.height >> 2); - int steps[] = {pyr[i * 4]->step, pyr[i * 4 + next * 4]->step, pyr[i * 4 + next * 8]->step}; - int i_cols = pyr[i * 4 + next * 8]->cols - (cascade->size.width >> 2); - int paddings[] = {pyr[i * 4]->step * 4 - i_cols * 4, - pyr[i * 4 + next * 4]->step * 2 - i_cols * 2, - pyr[i * 4 + next * 8]->step - i_cols}; - for (q = 0; q < (params.accurate ? 4 : 1); q++) - { - unsigned char *u8[] = {pyr[i * 4]->data.u8 + dx[q] * 2 + dy[q] * pyr[i * 4]->step * 2, pyr[i * 4 + next * 4]->data.u8 + dx[q] + dy[q] * pyr[i * 4 + next * 4]->step, pyr[i * 4 + next * 8 + q]->data.u8}; - for (y = 0; y < i_rows; y++) - { - for (x = 0; x < i_cols; x++) - { - float sum; - int flag = 1; - ccv_bbf_stage_classifier_t *classifier = cascade->stage_classifier; - for (j = 0; j < cascade->count; ++j, ++classifier) - { - sum = 0; - float *alpha = classifier->alpha; - ccv_bbf_feature_t *feature = classifier->feature; - for (k = 0; k < classifier->count; ++k, alpha += 2, ++feature) - sum += alpha[_ccv_run_bbf_feature(feature, steps, u8)]; - if (sum < classifier->threshold) - { - flag = 0; - break; - } - } - if (flag) - { - ccv_comp_t comp; - comp.rect = ccv_rect((int)((x * 4 + dx[q] * 2) * scale_x + 0.5), (int)((y * 4 + dy[q] * 2) * scale_y + 0.5), (int)(cascade->size.width * scale_x + 0.5), (int)(cascade->size.height * scale_y + 0.5)); - comp.neighbors = 1; - comp.classification.id = t; - comp.classification.confidence = sum; - ccv_array_push(seq, &comp); - } - u8[0] += 4; - u8[1] += 2; - u8[2] += 1; - } - u8[0] += paddings[0]; - u8[1] += paddings[1]; - u8[2] += paddings[2]; - } - } - scale_x *= scale; - scale_y *= scale; - } - - /* the following code from OpenCV's haar feature implementation */ - if (params.min_neighbors == 0) - { - for (i = 0; i < seq->rnum; i++) - { - ccv_comp_t *comp = (ccv_comp_t *)ccv_array_get(seq, i); - ccv_array_push(result_seq, comp); - } - } - else - { - idx_seq = 0; - ccv_array_clear(seq2); - // group retrieved rectangles in order to filter out noise - int ncomp = ccv_array_group(seq, &idx_seq, _ccv_is_equal_same_class, 0); - ccv_comp_t *comps = (ccv_comp_t *)ccmalloc((ncomp + 1) * sizeof(ccv_comp_t)); - memset(comps, 0, (ncomp + 1) * sizeof(ccv_comp_t)); - - // count number of neighbors - for (i = 0; i < seq->rnum; i++) - { - ccv_comp_t r1 = *(ccv_comp_t *)ccv_array_get(seq, i); - int idx = *(int *)ccv_array_get(idx_seq, i); - - if (comps[idx].neighbors == 0) - comps[idx].classification.confidence = r1.classification.confidence; - - ++comps[idx].neighbors; - - comps[idx].rect.x += r1.rect.x; - comps[idx].rect.y += r1.rect.y; - comps[idx].rect.width += r1.rect.width; - comps[idx].rect.height += r1.rect.height; - comps[idx].classification.id = r1.classification.id; - comps[idx].classification.confidence = ccv_max(comps[idx].classification.confidence, r1.classification.confidence); - } - - // calculate average bounding box - for (i = 0; i < ncomp; i++) - { - int n = comps[i].neighbors; - if (n >= params.min_neighbors) - { - ccv_comp_t comp; - comp.rect.x = (comps[i].rect.x * 2 + n) / (2 * n); - comp.rect.y = (comps[i].rect.y * 2 + n) / (2 * n); - comp.rect.width = (comps[i].rect.width * 2 + n) / (2 * n); - comp.rect.height = (comps[i].rect.height * 2 + n) / (2 * n); - comp.neighbors = comps[i].neighbors; - comp.classification.id = comps[i].classification.id; - comp.classification.confidence = comps[i].classification.confidence; - ccv_array_push(seq2, &comp); - } - } - - // filter out small face rectangles inside large face rectangles - for (i = 0; i < seq2->rnum; i++) - { - ccv_comp_t r1 = *(ccv_comp_t *)ccv_array_get(seq2, i); - int flag = 1; - - for (j = 0; j < seq2->rnum; j++) - { - ccv_comp_t r2 = *(ccv_comp_t *)ccv_array_get(seq2, j); - int distance = (int)(r2.rect.width * 0.25 + 0.5); - - if (i != j && - r1.classification.id == r2.classification.id && - r1.rect.x >= r2.rect.x - distance && - r1.rect.y >= r2.rect.y - distance && - r1.rect.x + r1.rect.width <= r2.rect.x + r2.rect.width + distance && - r1.rect.y + r1.rect.height <= r2.rect.y + r2.rect.height + distance && - (r2.neighbors > ccv_max(3, r1.neighbors) || r1.neighbors < 3)) - { - flag = 0; - break; - } - } - - if (flag) - ccv_array_push(result_seq, &r1); - } - ccv_array_free(idx_seq); - ccfree(comps); - } - } - - ccv_array_free(seq); - ccv_array_free(seq2); - - ccv_array_t *result_seq2; - /* the following code from OpenCV's haar feature implementation */ - if (params.flags & CCV_BBF_NO_NESTED) - { - result_seq2 = ccv_array_new(sizeof(ccv_comp_t), 64, 0); - idx_seq = 0; - // group retrieved rectangles in order to filter out noise - int ncomp = ccv_array_group(result_seq, &idx_seq, _ccv_is_equal, 0); - ccv_comp_t *comps = (ccv_comp_t *)ccmalloc((ncomp + 1) * sizeof(ccv_comp_t)); - memset(comps, 0, (ncomp + 1) * sizeof(ccv_comp_t)); - - // count number of neighbors - for (i = 0; i < result_seq->rnum; i++) - { - ccv_comp_t r1 = *(ccv_comp_t *)ccv_array_get(result_seq, i); - int idx = *(int *)ccv_array_get(idx_seq, i); - - if (comps[idx].neighbors == 0 || comps[idx].classification.confidence < r1.classification.confidence) - { - comps[idx].classification.confidence = r1.classification.confidence; - comps[idx].neighbors = 1; - comps[idx].rect = r1.rect; - comps[idx].classification.id = r1.classification.id; - } - } - - // calculate average bounding box - for (i = 0; i < ncomp; i++) - if (comps[i].neighbors) - ccv_array_push(result_seq2, &comps[i]); - - ccv_array_free(result_seq); - ccfree(comps); - } - else - { - result_seq2 = result_seq; - } - - for (i = 1; i < scale_upto + next * 2; i++) - ccv_matrix_free(pyr[i * 4]); - if (params.accurate) - for (i = next * 2; i < scale_upto + next * 2; i++) - { - ccv_matrix_free(pyr[i * 4 + 1]); - ccv_matrix_free(pyr[i * 4 + 2]); - ccv_matrix_free(pyr[i * 4 + 3]); - } - if (params.size.height != _cascade[0]->size.height || params.size.width != _cascade[0]->size.width) - ccv_matrix_free(pyr[0]); - - return result_seq2; -} - -ccv_bbf_classifier_cascade_t *ccv_bbf_read_classifier_cascade(const char *directory) -{ - char buf[1024]; - sprintf(buf, "%s/cascade.txt", directory); - int s, i; - FILE *r = fopen(buf, "r"); - if (r == 0) - return 0; - ccv_bbf_classifier_cascade_t *cascade = (ccv_bbf_classifier_cascade_t *)ccmalloc(sizeof(ccv_bbf_classifier_cascade_t)); - s = fscanf(r, "%d %d %d", &cascade->count, &cascade->size.width, &cascade->size.height); - assert(s > 0); - cascade->stage_classifier = (ccv_bbf_stage_classifier_t *)ccmalloc(cascade->count * sizeof(ccv_bbf_stage_classifier_t)); - for (i = 0; i < cascade->count; i++) - { - sprintf(buf, "%s/stage-%d.txt", directory, i); - if (_ccv_read_bbf_stage_classifier(buf, &cascade->stage_classifier[i]) < 0) - { - cascade->count = i; - break; - } - } - fclose(r); - return cascade; -} - -ccv_bbf_classifier_cascade_t *ccv_bbf_classifier_cascade_read_binary(char *s) -{ - int i; - ccv_bbf_classifier_cascade_t *cascade = (ccv_bbf_classifier_cascade_t *)ccmalloc(sizeof(ccv_bbf_classifier_cascade_t)); - memcpy(&cascade->count, s, sizeof(cascade->count)); - s += sizeof(cascade->count); - memcpy(&cascade->size.width, s, sizeof(cascade->size.width)); - s += sizeof(cascade->size.width); - memcpy(&cascade->size.height, s, sizeof(cascade->size.height)); - s += sizeof(cascade->size.height); - ccv_bbf_stage_classifier_t *classifier = cascade->stage_classifier = (ccv_bbf_stage_classifier_t *)ccmalloc(cascade->count * sizeof(ccv_bbf_stage_classifier_t)); - for (i = 0; i < cascade->count; i++, classifier++) - { - memcpy(&classifier->count, s, sizeof(classifier->count)); - s += sizeof(classifier->count); - memcpy(&classifier->threshold, s, sizeof(classifier->threshold)); - s += sizeof(classifier->threshold); - classifier->feature = (ccv_bbf_feature_t *)ccmalloc(classifier->count * sizeof(ccv_bbf_feature_t)); - classifier->alpha = (float *)ccmalloc(classifier->count * 2 * sizeof(float)); - memcpy(classifier->feature, s, classifier->count * sizeof(ccv_bbf_feature_t)); - s += classifier->count * sizeof(ccv_bbf_feature_t); - memcpy(classifier->alpha, s, classifier->count * 2 * sizeof(float)); - s += classifier->count * 2 * sizeof(float); - } - return cascade; -} - -int ccv_bbf_classifier_cascade_write_binary(ccv_bbf_classifier_cascade_t *cascade, char *s, int slen) -{ - int i; - int len = sizeof(cascade->count) + sizeof(cascade->size.width) + sizeof(cascade->size.height); - ccv_bbf_stage_classifier_t *classifier = cascade->stage_classifier; - for (i = 0; i < cascade->count; i++, classifier++) - len += sizeof(classifier->count) + sizeof(classifier->threshold) + classifier->count * sizeof(ccv_bbf_feature_t) + classifier->count * 2 * sizeof(float); - if (slen >= len) - { - memcpy(s, &cascade->count, sizeof(cascade->count)); - s += sizeof(cascade->count); - memcpy(s, &cascade->size.width, sizeof(cascade->size.width)); - s += sizeof(cascade->size.width); - memcpy(s, &cascade->size.height, sizeof(cascade->size.height)); - s += sizeof(cascade->size.height); - classifier = cascade->stage_classifier; - for (i = 0; i < cascade->count; i++, classifier++) - { - memcpy(s, &classifier->count, sizeof(classifier->count)); - s += sizeof(classifier->count); - memcpy(s, &classifier->threshold, sizeof(classifier->threshold)); - s += sizeof(classifier->threshold); - memcpy(s, classifier->feature, classifier->count * sizeof(ccv_bbf_feature_t)); - s += classifier->count * sizeof(ccv_bbf_feature_t); - memcpy(s, classifier->alpha, classifier->count * 2 * sizeof(float)); - s += classifier->count * 2 * sizeof(float); - } - } - return len; -} - -void ccv_bbf_classifier_cascade_free(ccv_bbf_classifier_cascade_t *cascade) -{ - int i; - for (i = 0; i < cascade->count; ++i) - { - ccfree(cascade->stage_classifier[i].feature); - ccfree(cascade->stage_classifier[i].alpha); - } - ccfree(cascade->stage_classifier); - ccfree(cascade); -} \ No newline at end of file