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 / ENDORSE(params.size.height); int wr = a->cols / ENDORSE(params.size.width); double scale = pow(2., 1. / (params.interval + 1.)); APPROX 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(ENDORSE(scale_upto + next * 2) * 4 * sizeof(ccv_dense_matrix_t*)); memset(pyr, 0, (scale_upto + next * 2) * 4 * sizeof(ccv_dense_matrix_t*)); if (ENDORSE(params.size.height != _cascade[0]->size.height || params.size.width != _cascade[0]->size.width)) ccv_resample(a, &pyr[0], 0, a->rows * ENDORSE(_cascade[0]->size.height / params.size.height), a->cols * ENDORSE(_cascade[0]->size.width / params.size.width), CCV_INTER_AREA); else pyr[0] = a; APPROX int i; int j, k, t, x, y, q; for (i = 1; ENDORSE(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; ENDORSE(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; ENDORSE(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]; APPROX float scale_x = (float) params.size.width / (float) cascade->size.width; APPROX float scale_y = (float) params.size.height / (float) cascade->size.height; ccv_array_clear(seq); for (i = 0; ENDORSE(i < scale_upto); i++) { APPROX int dx[] = {0, 1, 0, 1}; APPROX int dy[] = {0, 0, 1, 1}; APPROX int i_rows = pyr[i * 4 + next * 8]->rows - ENDORSE(cascade->size.height >> 2); APPROX int steps[] = { pyr[i * 4]->step, pyr[i * 4 + next * 4]->step, pyr[i * 4 + next * 8]->step }; APPROX int i_cols = pyr[i * 4 + next * 8]->cols - ENDORSE(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++) { APPROX 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; ENDORSE(y < i_rows); y++) { for (x = 0; ENDORSE(x < i_cols); x++) { APPROX float sum; APPROX int flag = 1; ccv_bbf_stage_classifier_t* classifier = cascade->stage_classifier; for (j = 0; j < ENDORSE(cascade->count); ++j, ++classifier) { sum = 0; APPROX float* alpha = classifier->alpha; ccv_bbf_feature_t* feature = classifier->feature; for (k = 0; k < ENDORSE(classifier->count); ++k, alpha += 2, ++feature) sum += alpha[_ccv_run_bbf_feature(feature, ENDORSE(steps), u8)]; if (ENDORSE(sum) < ENDORSE(classifier->threshold)) { flag = 0; break; } } if (ENDORSE(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; ENDORSE(i < seq->rnum); i++) { ccv_comp_t* comp = (ccv_comp_t*)ENDORSE(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; ENDORSE(i < seq->rnum); i++) { ccv_comp_t r1 = *(ccv_comp_t*)ENDORSE(ccv_array_get(seq, i)); int idx = *(int*)ENDORSE(ccv_array_get(idx_seq, i)); if (ENDORSE(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; ENDORSE(i < ncomp); i++) { int n = ENDORSE(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; ENDORSE(i < seq2->rnum); i++) { ccv_comp_t r1 = *(ccv_comp_t*)ENDORSE(ccv_array_get(seq2, i)); APPROX int flag = 1; for(j = 0; ENDORSE(j < seq2->rnum); j++) { ccv_comp_t r2 = *(ccv_comp_t*)ENDORSE(ccv_array_get(seq2, j)); APPROX int distance = (int)(r2.rect.width * 0.25 + 0.5); if(ENDORSE(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(ENDORSE(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; ENDORSE(i < result_seq->rnum); i++) { ccv_comp_t r1 = *(ccv_comp_t*)ENDORSE(ccv_array_get(result_seq, i)); int idx = *(int*)ENDORSE(ccv_array_get(idx_seq, i)); if (ENDORSE(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; ENDORSE(i < ncomp); i++) if(ENDORSE(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; ENDORSE(i < scale_upto + next * 2); i++) ccv_matrix_free(pyr[i * 4]); if (params.accurate) for (i = next * 2; ENDORSE(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 (ENDORSE(params.size.height != _cascade[0]->size.height || params.size.width != _cascade[0]->size.width)) ccv_matrix_free(pyr[0]); return result_seq2; }
/* this code is a rewrite from OpenCV's legendary Lucas-Kanade optical flow implementation */ void ccv_optical_flow_lucas_kanade(ccv_dense_matrix_t* a, ccv_dense_matrix_t* b, ccv_array_t* point_a, ccv_array_t** point_b, ccv_size_t win_size, int level, double min_eigen) { assert(a && b && a->rows == b->rows && a->cols == b->cols); assert(CCV_GET_CHANNEL(a->type) == CCV_GET_CHANNEL(b->type) && CCV_GET_DATA_TYPE(a->type) == CCV_GET_DATA_TYPE(b->type)); assert(CCV_GET_CHANNEL(a->type) == 1); assert(CCV_GET_DATA_TYPE(a->type) == CCV_8U); assert(point_a->rnum > 0); level = ccv_clamp(level + 1, 1, (int)(log((double)ccv_min(a->rows, a->cols) / ccv_max(win_size.width * 2, win_size.height * 2)) / log(2.0) + 0.5)); ccv_declare_derived_signature(sig, a->sig != 0 && b->sig != 0 && point_a->sig != 0, ccv_sign_with_format(128, "ccv_optical_flow_lucas_kanade(%d,%d,%d,%la)", win_size.width, win_size.height, level, min_eigen), a->sig, b->sig, point_a->sig, CCV_EOF_SIGN); ccv_array_t* seq = *point_b = ccv_array_new(sizeof(ccv_decimal_point_with_status_t), point_a->rnum, sig); ccv_object_return_if_cached(, seq); seq->rnum = point_a->rnum; ccv_dense_matrix_t** pyr_a = (ccv_dense_matrix_t**)malloc(sizeof(ccv_dense_matrix_t*) * level); ccv_dense_matrix_t** pyr_a_dx = (ccv_dense_matrix_t**)malloc(sizeof(ccv_dense_matrix_t*) * level); ccv_dense_matrix_t** pyr_a_dy = (ccv_dense_matrix_t**)malloc(sizeof(ccv_dense_matrix_t*) * level); ccv_dense_matrix_t** pyr_b = (ccv_dense_matrix_t**)malloc(sizeof(ccv_dense_matrix_t*) * level); int i, j, t, x, y; /* generating image pyramid */ pyr_a[0] = a; pyr_a_dx[0] = pyr_a_dy[0] = 0; ccv_sobel(pyr_a[0], &pyr_a_dx[0], 0, 3, 0); ccv_sobel(pyr_a[0], &pyr_a_dy[0], 0, 0, 3); pyr_b[0] = b; for (i = 1; i < level; i++) { pyr_a[i] = pyr_a_dx[i] = pyr_a_dy[i] = pyr_b[i] = 0; ccv_sample_down(pyr_a[i - 1], &pyr_a[i], 0, 0, 0); ccv_sobel(pyr_a[i], &pyr_a_dx[i], 0, 3, 0); ccv_sobel(pyr_a[i], &pyr_a_dy[i], 0, 0, 3); ccv_sample_down(pyr_b[i - 1], &pyr_b[i], 0, 0, 0); } int* wi = (int*)malloc(sizeof(int) * win_size.width * win_size.height); int* widx = (int*)malloc(sizeof(int) * win_size.width * win_size.height); int* widy = (int*)malloc(sizeof(int) * win_size.width * win_size.height); ccv_decimal_point_t half_win = ccv_decimal_point((win_size.width - 1) * 0.5f, (win_size.height - 1) * 0.5f); const int W_BITS14 = 14, W_BITS7 = 7, W_BITS9 = 9; const float FLT_SCALE = 1.0f / (1 << 25); // clean up status to 1 for (i = 0; i < point_a->rnum; i++) { ccv_decimal_point_with_status_t* point_with_status = (ccv_decimal_point_with_status_t*)ccv_array_get(seq, i); point_with_status->status = 1; } int prev_rows, prev_cols; for (t = level - 1; t >= 0; t--) { ccv_dense_matrix_t* a = pyr_a[t]; ccv_dense_matrix_t* adx = pyr_a_dx[t]; ccv_dense_matrix_t* ady = pyr_a_dy[t]; assert(CCV_GET_DATA_TYPE(adx->type) == CCV_32S); assert(CCV_GET_DATA_TYPE(ady->type) == CCV_32S); ccv_dense_matrix_t* b = pyr_b[t]; for (i = 0; i < point_a->rnum; i++) { ccv_decimal_point_t prev_point = *(ccv_decimal_point_t*)ccv_array_get(point_a, i); ccv_decimal_point_with_status_t* point_with_status = (ccv_decimal_point_with_status_t*)ccv_array_get(seq, i); prev_point.x = prev_point.x / (float)(1 << t); prev_point.y = prev_point.y / (float)(1 << t); ccv_decimal_point_t next_point; if (t == level - 1) next_point = prev_point; else { next_point.x = point_with_status->point.x * 2 + (a->cols - prev_cols * 2) * 0.5; next_point.y = point_with_status->point.y * 2 + (a->rows - prev_rows * 2) * 0.5; } point_with_status->point = next_point; prev_point.x -= half_win.x; prev_point.y -= half_win.y; ccv_point_t iprev_point = ccv_point((int)prev_point.x, (int)prev_point.y); if (iprev_point.x < 0 || iprev_point.x >= a->cols - win_size.width - 1 || iprev_point.y < 0 || iprev_point.y >= a->rows - win_size.height - 1) { if (t == 0) point_with_status->status = 0; continue; } float xd = prev_point.x - iprev_point.x; float yd = prev_point.y - iprev_point.y; int iw00 = (int)((1 - xd) * (1 - yd) * (1 << W_BITS14) + 0.5); int iw01 = (int)(xd * (1 - yd) * (1 << W_BITS14) + 0.5); int iw10 = (int)((1 - xd) * yd * (1 << W_BITS14) + 0.5); int iw11 = (1 << W_BITS14) - iw00 - iw01 - iw10; float a11 = 0, a12 = 0, a22 = 0; unsigned char* a_ptr = (unsigned char*)ccv_get_dense_matrix_cell_by(CCV_C1 | CCV_8U, a, iprev_point.y, iprev_point.x, 0); int* adx_ptr = (int*)ccv_get_dense_matrix_cell_by(CCV_C1 | CCV_32S, adx, iprev_point.y, iprev_point.x, 0); int* ady_ptr = (int*)ccv_get_dense_matrix_cell_by(CCV_C1 | CCV_32S, ady, iprev_point.y, iprev_point.x, 0); int* wi_ptr = wi; int* widx_ptr = widx; int* widy_ptr = widy; for (y = 0; y < win_size.height; y++) { for (x = 0; x < win_size.width; x++) { wi_ptr[x] = ccv_descale(a_ptr[x] * iw00 + a_ptr[x + 1] * iw01 + a_ptr[x + a->step] * iw10 + a_ptr[x + a->step + 1] * iw11, W_BITS7); // because we use 3x3 sobel, which scaled derivative up by 4 widx_ptr[x] = ccv_descale(adx_ptr[x] * iw00 + adx_ptr[x + 1] * iw01 + adx_ptr[x + adx->cols] * iw10 + adx_ptr[x + adx->cols + 1] * iw11, W_BITS9); widy_ptr[x] = ccv_descale(ady_ptr[x] * iw00 + ady_ptr[x + 1] * iw01 + ady_ptr[x + ady->cols] + iw10 + ady_ptr[x + ady->cols + 1] * iw11, W_BITS9); a11 += (float)(widx_ptr[x] * widx_ptr[x]); a12 += (float)(widx_ptr[x] * widy_ptr[x]); a22 += (float)(widy_ptr[x] * widy_ptr[x]); } a_ptr += a->step; adx_ptr += adx->cols; ady_ptr += ady->cols; wi_ptr += win_size.width; widx_ptr += win_size.width; widy_ptr += win_size.width; } a11 *= FLT_SCALE; a12 *= FLT_SCALE; a22 *= FLT_SCALE; float D = a11 * a22 - a12 * a12; float eigen = (a22 + a11 - sqrtf((a11 - a22) * (a11 - a22) + 4.0f * a12 * a12)) / (2 * win_size.width * win_size.height); if (eigen < min_eigen || D < FLT_EPSILON) { if (t == 0) point_with_status->status = 0; continue; } D = 1.0f / D; next_point.x -= half_win.x; next_point.y -= half_win.y; ccv_decimal_point_t prev_delta; for (j = 0; j < LK_MAX_ITER; j++) { ccv_point_t inext_point = ccv_point((int)next_point.x, (int)next_point.y); if (inext_point.x < 0 || inext_point.x >= a->cols - win_size.width - 1 || inext_point.y < 0 || inext_point.y >= a->rows - win_size.height - 1) break; float xd = next_point.x - inext_point.x; float yd = next_point.y - inext_point.y; int iw00 = (int)((1 - xd) * (1 - yd) * (1 << W_BITS14) + 0.5); int iw01 = (int)(xd * (1 - yd) * (1 << W_BITS14) + 0.5); int iw10 = (int)((1 - xd) * yd * (1 << W_BITS14) + 0.5); int iw11 = (1 << W_BITS14) - iw00 - iw01 - iw10; float b1 = 0, b2 = 0; unsigned char* b_ptr = (unsigned char*)ccv_get_dense_matrix_cell_by(CCV_C1 | CCV_8U, b, inext_point.y, inext_point.x, 0); int* wi_ptr = wi; int* widx_ptr = widx; int* widy_ptr = widy; for (y = 0; y < win_size.height; y++) { for (x = 0; x < win_size.width; x++) { int diff = ccv_descale(b_ptr[x] * iw00 + b_ptr[x + 1] * iw01 + b_ptr[x + b->step] * iw10 + b_ptr[x + b->step + 1] * iw11, W_BITS7) - wi_ptr[x]; b1 += (float)(diff * widx_ptr[x]); b2 += (float)(diff * widy_ptr[x]); } b_ptr += b->step; wi_ptr += win_size.width; widx_ptr += win_size.width; widy_ptr += win_size.width; } b1 *= FLT_SCALE; b2 *= FLT_SCALE; ccv_decimal_point_t delta = ccv_decimal_point((a12 * b2 - a22 * b1) * D, (a12 * b1 - a11 * b2) * D); next_point.x += delta.x; next_point.y += delta.y; if (delta.x * delta.x + delta.y * delta.y < LK_EPSILON) break; if (j > 0 && fabs(prev_delta.x - delta.x) < 0.01 && fabs(prev_delta.y - delta.y) < 0.01) { next_point.x -= delta.x * 0.5; next_point.y -= delta.y * 0.5; break; } prev_delta = delta; } ccv_point_t inext_point = ccv_point((int)next_point.x, (int)next_point.y); if (inext_point.x < 0 || inext_point.x >= a->cols - win_size.width - 1 || inext_point.y < 0 || inext_point.y >= a->rows - win_size.height - 1) point_with_status->status = 0; else { point_with_status->point.x = next_point.x + half_win.x; point_with_status->point.y = next_point.y + half_win.y; } } prev_rows = a->rows; prev_cols = a->cols; ccv_matrix_free(adx); ccv_matrix_free(ady); if (t > 0) { ccv_matrix_free(a); ccv_matrix_free(b); } } free(widy); free(widx); free(wi); free(pyr_b); free(pyr_a_dy); free(pyr_a_dx); free(pyr_a); }