| File: | ccv_swt.c |
| Warning: | line 463, column 26 The left operand of '==' is a garbage value |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | #include "ccv.h" | |||
| 2 | #include "ccv_internal.h" | |||
| 3 | ||||
| 4 | const ccv_swt_param_t ccv_swt_default_params = { | |||
| 5 | .interval = 1, | |||
| 6 | .same_word_thresh = { 0.1, 0.8 }, | |||
| 7 | .min_neighbors = 1, | |||
| 8 | .scale_invariant = 0, | |||
| 9 | .size = 3, | |||
| 10 | .low_thresh = 124, | |||
| 11 | .high_thresh = 204, | |||
| 12 | .max_height = 300, | |||
| 13 | .min_height = 8, | |||
| 14 | .min_area = 38, | |||
| 15 | .letter_occlude_thresh = 3, | |||
| 16 | .aspect_ratio = 8, | |||
| 17 | .std_ratio = 0.83, | |||
| 18 | .thickness_ratio = 1.5, | |||
| 19 | .height_ratio = 1.7, | |||
| 20 | .intensity_thresh = 31, | |||
| 21 | .distance_ratio = 2.9, | |||
| 22 | .intersect_ratio = 1.3, | |||
| 23 | .letter_thresh = 3, | |||
| 24 | .elongate_ratio = 1.9, | |||
| 25 | .breakdown = 1, | |||
| 26 | .breakdown_ratio = 1.0, | |||
| 27 | }; | |||
| 28 | ||||
| 29 | static inline CCV_IMPLEMENT_MEDIAN(_ccv_swt_median, int)int _ccv_swt_median(int* buf, int low, int high) { int w; int middle, ll, hh; int median = (low + high) / 2; for (;;) { if (high <= low) return buf[median]; if (high == low + 1) { if (buf[low] > buf[high]) ((w) = (buf[low]), (buf[low]) = (buf [high]), (buf[high]) = (w)); return buf[median]; } middle = ( low + high) / 2; if (buf[middle] > buf[high]) ((w) = (buf[ middle]), (buf[middle]) = (buf[high]), (buf[high]) = (w)); if (buf[low] > buf[high]) ((w) = (buf[low]), (buf[low]) = (buf [high]), (buf[high]) = (w)); if (buf[middle] > buf[low]) ( (w) = (buf[middle]), (buf[middle]) = (buf[low]), (buf[low]) = (w)); ((w) = (buf[middle]), (buf[middle]) = (buf[low + 1]), ( buf[low + 1]) = (w)); ll = low + 1; hh = high; for (;;) { do ll ++; while (buf[low] > buf[ll]); do hh--; while (buf[hh] > buf[low]); if (hh < ll) break; ((w) = (buf[ll]), (buf[ll] ) = (buf[hh]), (buf[hh]) = (w)); } ((w) = (buf[low]), (buf[low ]) = (buf[hh]), (buf[hh]) = (w)); if (hh <= median) low = ll ; else if (hh >= median) high = hh - 1; } } | |||
| 30 | ||||
| 31 | typedef struct { | |||
| 32 | int x0, x1, y0, y1; | |||
| 33 | int w; | |||
| 34 | } ccv_swt_stroke_t; | |||
| 35 | ||||
| 36 | #define less_than(s1, s2, aux) ((s1).w < (s2).w) | |||
| 37 | static CCV_IMPLEMENT_QSORT(_ccv_swt_stroke_qsort, ccv_swt_stroke_t, less_than)void _ccv_swt_stroke_qsort(ccv_swt_stroke_t *array, size_t total , int aux) { int isort_thresh = 7; ccv_swt_stroke_t t; int sp = 0; struct { ccv_swt_stroke_t *lb; ccv_swt_stroke_t *ub; } stack [48]; if( total <= 1 ) return; stack[0].lb = array; stack[ 0].ub = array + (total - 1); while( sp >= 0 ) { ccv_swt_stroke_t * left = stack[sp].lb; ccv_swt_stroke_t* right = stack[sp--]. ub; for(;;) { int i, n = (int)(right - left) + 1, m; ccv_swt_stroke_t * ptr; ccv_swt_stroke_t* ptr2; if( n <= isort_thresh ) { insert_sort : for( ptr = left + 1; ptr <= right; ptr++ ) { for( ptr2 = ptr; ptr2 > left && less_than(ptr2[0],ptr2[-1], aux ); ptr2--) (((t)) = ((ptr2[0])), ((ptr2[0])) = ((ptr2[-1])), ( (ptr2[-1])) = ((t))); } break; } else { ccv_swt_stroke_t* left0 ; ccv_swt_stroke_t* left1; ccv_swt_stroke_t* right0; ccv_swt_stroke_t * right1; ccv_swt_stroke_t* pivot; ccv_swt_stroke_t* a; ccv_swt_stroke_t * b; ccv_swt_stroke_t* c; int swap_cnt = 0; left0 = left; right0 = right; pivot = left + (n/2); if( n > 40 ) { int d = n / 8; a = left, b = left + d, c = left + 2*d; left = less_than( *a, *b, aux) ? (less_than(*b, *c, aux) ? b : (less_than(*a, * c, aux) ? c : a)) : (less_than(*c, *b, aux) ? b : (less_than( *a, *c, aux) ? a : c)); a = pivot - d, b = pivot, c = pivot + d; pivot = less_than(*a, *b, aux) ? (less_than(*b, *c, aux) ? b : (less_than(*a, *c, aux) ? c : a)) : (less_than(*c, *b, aux ) ? b : (less_than(*a, *c, aux) ? a : c)); a = right - 2*d, b = right - d, c = right; right = less_than(*a, *b, aux) ? (less_than (*b, *c, aux) ? b : (less_than(*a, *c, aux) ? c : a)) : (less_than (*c, *b, aux) ? b : (less_than(*a, *c, aux) ? a : c)); } a = left , b = pivot, c = right; pivot = less_than(*a, *b, aux) ? (less_than (*b, *c, aux) ? b : (less_than(*a, *c, aux) ? c : a)) : (less_than (*c, *b, aux) ? b : (less_than(*a, *c, aux) ? a : c)); if( pivot != left0 ) { (((t)) = ((*pivot)), ((*pivot)) = ((*left0)), ( (*left0)) = ((t))); pivot = left0; } left = left1 = left0 + 1 ; right = right1 = right0; for(;;) { while( left <= right && !less_than(*pivot, *left, aux) ) { if( !less_than(*left, *pivot , aux) ) { if( left > left1 ) (((t)) = ((*left1)), ((*left1 )) = ((*left)), ((*left)) = ((t))); swap_cnt = 1; left1++; } left ++; } while( left <= right && !less_than(*right, * pivot, aux) ) { if( !less_than(*pivot, *right, aux) ) { if( right < right1 ) (((t)) = ((*right1)), ((*right1)) = ((*right)) , ((*right)) = ((t))); swap_cnt = 1; right1--; } right--; } if ( left > right ) break; (((t)) = ((*left)), ((*left)) = (( *right)), ((*right)) = ((t))); swap_cnt = 1; left++; right--; } if( swap_cnt == 0 ) { left = left0, right = right0; goto insert_sort ; } n = ({ typeof ((int)(left1 - left0)) _a = ((int)(left1 - left0 )); typeof ((int)(left - left1)) _b = ((int)(left - left1)); ( _a < _b) ? _a : _b; }); for( i = 0; i < n; i++ ) (((t)) = ((left0[i])), ((left0[i])) = ((left[i-n])), ((left[i-n])) = ((t))); n = ({ typeof ((int)(right0 - right1)) _a = ((int)(right0 - right1)); typeof ((int)(right1 - right)) _b = ((int)(right1 - right)); (_a < _b) ? _a : _b; }); for( i = 0; i < n; i++ ) (((t)) = ((left[i])), ((left[i])) = ((right0[i-n+1])), ((right0[i-n+1])) = ((t))); n = (int)(left - left1); m = (int )(right1 - right); if( n > 1 ) { if( m > 1 ) { if( n > m ) { stack[++sp].lb = left0; stack[sp].ub = left0 + n - 1; left = right0 - m + 1, right = right0; } else { stack[++sp].lb = right0 - m + 1; stack[sp].ub = right0; left = left0, right = left0 + n - 1; } } else left = left0, right = left0 + n - 1; } else if ( m > 1 ) left = right0 - m + 1, right = right0; else break ; } } } } | |||
| 38 | #undef less_than | |||
| 39 | ||||
| 40 | /* ccv_swt is only the method to generate stroke width map */ | |||
| 41 | static void _ccv_swt(ccv_dense_matrix_t* a, ccv_dense_matrix_t** b, int type, ccv_swt_param_t params, ccv_dense_matrix_t* const _c, ccv_dense_matrix_t* const _dx, ccv_dense_matrix_t* const _dy) | |||
| 42 | { | |||
| 43 | assert(a->type & CCV_C1)((void) sizeof ((a->type & CCV_C1) ? 1 : 0), __extension__ ({ if (a->type & CCV_C1) ; else __assert_fail ("a->type & CCV_C1" , "ccv_swt.c", 43, __extension__ __PRETTY_FUNCTION__); })); | |||
| 44 | ccv_declare_derived_signature(sig, a->sig != 0, ccv_sign_with_format(64, "ccv_swt(%d,%d,%d,%d)", params.direction, params.size, params.low_thresh, params.high_thresh), a->sig, CCV_EOF_SIGN)char _ccv_identifier_44[(64)]; memset(_ccv_identifier_44, 0, ( 64)); snprintf(_ccv_identifier_44, (64), ("ccv_swt(%d,%d,%d,%d)" ), params.direction, params.size, params.low_thresh, params.high_thresh ); size_t _ccv_string_size_44 = (64);; uint64_t sig = (a-> sig != 0) ? ccv_cache_generate_signature(_ccv_identifier_44, _ccv_string_size_44 , a->sig, ((uint64_t)0)) : 0;; | |||
| 45 | type = (type == 0) ? CCV_32S | CCV_C1 : CCV_GET_DATA_TYPE(type)((type) & 0xFF000) | CCV_C1; | |||
| 46 | ccv_dense_matrix_t* db = *b = ccv_dense_matrix_renew(*b, a->rows, a->cols, CCV_C1 | CCV_ALL_DATA_TYPE(CCV_8U | CCV_32S | CCV_32F | CCV_64S | CCV_64F | CCV_16F | CCV_QX ), type, sig); | |||
| 47 | ccv_object_return_if_cached(, db){ if ((!(db) || (((int*)(db))[0] & CCV_GARBAGE)) && (!(0) || (((int*)(0))[0] & CCV_GARBAGE)) && (!(0 ) || (((int*)(0))[0] & CCV_GARBAGE)) && (!(0) || ( ((int*)(0))[0] & CCV_GARBAGE)) && (!(0) || (((int *)(0))[0] & CCV_GARBAGE)) && (!(0) || (((int*)(0) )[0] & CCV_GARBAGE)) && (!(0) || (((int*)(0))[0] & CCV_GARBAGE)) && (!(0) || (((int*)(0))[0] & CCV_GARBAGE )) && (!(0) || (((int*)(0))[0] & CCV_GARBAGE)) && (!(0) || (((int*)(0))[0] & CCV_GARBAGE)) && (!(0 ) || (((int*)(0))[0] & CCV_GARBAGE)) && (!(0) || ( ((int*)(0))[0] & CCV_GARBAGE)) && (!(0) || (((int *)(0))[0] & CCV_GARBAGE)) && (!(0) || (((int*)(0) )[0] & CCV_GARBAGE)) && (!(0) || (((int*)(0))[0] & CCV_GARBAGE)) && (!(0) || (((int*)(0))[0] & CCV_GARBAGE )) && (!(0) || (((int*)(0))[0] & CCV_GARBAGE)) && (!(0) || (((int*)(0))[0] & CCV_GARBAGE)) && (!(0 ) || (((int*)(0))[0] & CCV_GARBAGE)) && (!(0) || ( ((int*)(0))[0] & CCV_GARBAGE)) && (!(0) || (((int *)(0))[0] & CCV_GARBAGE)) && (!(0) || (((int*)(0) )[0] & CCV_GARBAGE)) && (!(0) || (((int*)(0))[0] & CCV_GARBAGE)) && (!(0) || (((int*)(0))[0] & CCV_GARBAGE )) && (!(0) || (((int*)(0))[0] & CCV_GARBAGE)) && (!(0) || (((int*)(0))[0] & CCV_GARBAGE)) && (!(0 ) || (((int*)(0))[0] & CCV_GARBAGE)) && (!(0) || ( ((int*)(0))[0] & CCV_GARBAGE)) && (!(0) || (((int *)(0))[0] & CCV_GARBAGE)) && (!(0) || (((int*)(0) )[0] & CCV_GARBAGE)) && (!(0) || (((int*)(0))[0] & CCV_GARBAGE)) && (!(0) || (((int*)(0))[0] & CCV_GARBAGE )) && (!(0) || (((int*)(0))[0] & CCV_GARBAGE)) && (!(0) || (((int*)(0))[0] & CCV_GARBAGE)) && (!(0 ) || (((int*)(0))[0] & CCV_GARBAGE)) && (!(0) || ( ((int*)(0))[0] & CCV_GARBAGE)) && (!(0) || (((int *)(0))[0] & CCV_GARBAGE)) && (!(0) || (((int*)(0) )[0] & CCV_GARBAGE)) && (!(0) || (((int*)(0))[0] & CCV_GARBAGE)) && (!(0) || (((int*)(0))[0] & CCV_GARBAGE )) && (!(0) || (((int*)(0))[0] & CCV_GARBAGE)) && (!(0) || (((int*)(0))[0] & CCV_GARBAGE)) && (!(0 ) || (((int*)(0))[0] & CCV_GARBAGE)) && (!(0) || ( ((int*)(0))[0] & CCV_GARBAGE)) && (!(0) || (((int *)(0))[0] & CCV_GARBAGE)) && (!(0) || (((int*)(0) )[0] & CCV_GARBAGE)) && (!(0) || (((int*)(0))[0] & CCV_GARBAGE)) && (!(0) || (((int*)(0))[0] & CCV_GARBAGE )) && (!(0) || (((int*)(0))[0] & CCV_GARBAGE)) && (!(0) || (((int*)(0))[0] & CCV_GARBAGE)) && (!(0 ) || (((int*)(0))[0] & CCV_GARBAGE)) && (!(0) || ( ((int*)(0))[0] & CCV_GARBAGE)) && (!(0) || (((int *)(0))[0] & CCV_GARBAGE)) && (!(0) || (((int*)(0) )[0] & CCV_GARBAGE)) && (!(0) || (((int*)(0))[0] & CCV_GARBAGE)) && (!(0) || (((int*)(0))[0] & CCV_GARBAGE )) && (!(0) || (((int*)(0))[0] & CCV_GARBAGE)) && (!(0) || (((int*)(0))[0] & CCV_GARBAGE)) && (!(0 ) || (((int*)(0))[0] & CCV_GARBAGE)) && (!(0) || ( ((int*)(0))[0] & CCV_GARBAGE)) && (!(0) || (((int *)(0))[0] & CCV_GARBAGE)) && (!(0) || (((int*)(0) )[0] & CCV_GARBAGE)) && (!(0) || (((int*)(0))[0] & CCV_GARBAGE))) { (void)((db) && (((int*)(db))[0] &= ~CCV_GARBAGE));(void)((0) && (((int*)(0))[0] &= ~ CCV_GARBAGE));(void)((0) && (((int*)(0))[0] &= ~CCV_GARBAGE ));(void)((0) && (((int*)(0))[0] &= ~CCV_GARBAGE) );(void)((0) && (((int*)(0))[0] &= ~CCV_GARBAGE)) ;(void)((0) && (((int*)(0))[0] &= ~CCV_GARBAGE)); (void)((0) && (((int*)(0))[0] &= ~CCV_GARBAGE));( void)((0) && (((int*)(0))[0] &= ~CCV_GARBAGE));(void )((0) && (((int*)(0))[0] &= ~CCV_GARBAGE));(void) ((0) && (((int*)(0))[0] &= ~CCV_GARBAGE));(void)( (0) && (((int*)(0))[0] &= ~CCV_GARBAGE));(void)(( 0) && (((int*)(0))[0] &= ~CCV_GARBAGE));(void)((0 ) && (((int*)(0))[0] &= ~CCV_GARBAGE));(void)((0) && (((int*)(0))[0] &= ~CCV_GARBAGE));(void)((0) && (((int*)(0))[0] &= ~CCV_GARBAGE));(void)((0) && ( ((int*)(0))[0] &= ~CCV_GARBAGE));(void)((0) && (( (int*)(0))[0] &= ~CCV_GARBAGE));(void)((0) && ((( int*)(0))[0] &= ~CCV_GARBAGE));(void)((0) && (((int *)(0))[0] &= ~CCV_GARBAGE));(void)((0) && (((int* )(0))[0] &= ~CCV_GARBAGE));(void)((0) && (((int*) (0))[0] &= ~CCV_GARBAGE));(void)((0) && (((int*)( 0))[0] &= ~CCV_GARBAGE));(void)((0) && (((int*)(0 ))[0] &= ~CCV_GARBAGE));(void)((0) && (((int*)(0) )[0] &= ~CCV_GARBAGE));(void)((0) && (((int*)(0)) [0] &= ~CCV_GARBAGE));(void)((0) && (((int*)(0))[ 0] &= ~CCV_GARBAGE));(void)((0) && (((int*)(0))[0 ] &= ~CCV_GARBAGE));(void)((0) && (((int*)(0))[0] &= ~CCV_GARBAGE));(void)((0) && (((int*)(0))[0] &= ~CCV_GARBAGE));(void)((0) && (((int*)(0))[0] &= ~ CCV_GARBAGE));(void)((0) && (((int*)(0))[0] &= ~CCV_GARBAGE ));(void)((0) && (((int*)(0))[0] &= ~CCV_GARBAGE) );(void)((0) && (((int*)(0))[0] &= ~CCV_GARBAGE)) ;(void)((0) && (((int*)(0))[0] &= ~CCV_GARBAGE)); (void)((0) && (((int*)(0))[0] &= ~CCV_GARBAGE));( void)((0) && (((int*)(0))[0] &= ~CCV_GARBAGE));(void )((0) && (((int*)(0))[0] &= ~CCV_GARBAGE));(void) ((0) && (((int*)(0))[0] &= ~CCV_GARBAGE));(void)( (0) && (((int*)(0))[0] &= ~CCV_GARBAGE));(void)(( 0) && (((int*)(0))[0] &= ~CCV_GARBAGE));(void)((0 ) && (((int*)(0))[0] &= ~CCV_GARBAGE));(void)((0) && (((int*)(0))[0] &= ~CCV_GARBAGE));(void)((0) && (((int*)(0))[0] &= ~CCV_GARBAGE));(void)((0) && ( ((int*)(0))[0] &= ~CCV_GARBAGE));(void)((0) && (( (int*)(0))[0] &= ~CCV_GARBAGE));(void)((0) && ((( int*)(0))[0] &= ~CCV_GARBAGE));(void)((0) && (((int *)(0))[0] &= ~CCV_GARBAGE));(void)((0) && (((int* )(0))[0] &= ~CCV_GARBAGE));(void)((0) && (((int*) (0))[0] &= ~CCV_GARBAGE));(void)((0) && (((int*)( 0))[0] &= ~CCV_GARBAGE));(void)((0) && (((int*)(0 ))[0] &= ~CCV_GARBAGE));(void)((0) && (((int*)(0) )[0] &= ~CCV_GARBAGE));(void)((0) && (((int*)(0)) [0] &= ~CCV_GARBAGE));(void)((0) && (((int*)(0))[ 0] &= ~CCV_GARBAGE));(void)((0) && (((int*)(0))[0 ] &= ~CCV_GARBAGE));(void)((0) && (((int*)(0))[0] &= ~CCV_GARBAGE));(void)((0) && (((int*)(0))[0] &= ~CCV_GARBAGE));(void)((0) && (((int*)(0))[0] &= ~ CCV_GARBAGE));(void)((0) && (((int*)(0))[0] &= ~CCV_GARBAGE ));(void)((0) && (((int*)(0))[0] &= ~CCV_GARBAGE) );(void)((0) && (((int*)(0))[0] &= ~CCV_GARBAGE)) ;(void)((0) && (((int*)(0))[0] &= ~CCV_GARBAGE)); (void)((0) && (((int*)(0))[0] &= ~CCV_GARBAGE));; ; return ; } }; | |||
| 48 | ccv_dense_matrix_t* c = _c; | |||
| 49 | if (!c) | |||
| 50 | { | |||
| 51 | ccv_dense_matrix_t* cc = 0; | |||
| 52 | ccv_canny(a, &cc, 0, params.size, params.low_thresh, params.high_thresh); | |||
| 53 | ccv_close_outline(cc, &c, 0); | |||
| 54 | ccv_matrix_free(cc); | |||
| 55 | } | |||
| 56 | ccv_dense_matrix_t* dx = _dx; | |||
| 57 | if (!dx) | |||
| 58 | ccv_sobel(a, &dx, 0, params.size, 0); | |||
| 59 | ccv_dense_matrix_t* dy = _dy; | |||
| 60 | if (!dy) | |||
| 61 | ccv_sobel(a, &dy, 0, 0, params.size); | |||
| 62 | int i, j, k, w; | |||
| 63 | int* buf = (int*)alloca(sizeof(int) * ccv_max(a->cols, a->rows))__builtin_alloca (sizeof(int) * ({ typeof (a->cols) _a = ( a->cols); typeof (a->rows) _b = (a->rows); (_a > _b ) ? _a : _b; })); | |||
| 64 | ccv_array_t* strokes = ccv_array_new(sizeof(ccv_swt_stroke_t), 64, 0); | |||
| 65 | unsigned char* b_ptr = db->data.u8; | |||
| 66 | unsigned char* c_ptr = c->data.u8; | |||
| 67 | unsigned char* dx_ptr = dx->data.u8; | |||
| 68 | unsigned char* dy_ptr = dy->data.u8; | |||
| 69 | ccv_zero(db); | |||
| 70 | int dx5[] = {-1, 0, 1, 0, 0}; | |||
| 71 | int dy5[] = {0, 0, 0, -1, 1}; | |||
| 72 | int dx9[] = {-1, 0, 1, -1, 0, 1, -1, 0, 1}; | |||
| 73 | int dy9[] = {0, 0, 0, -1, -1, -1, 1, 1, 1}; | |||
| 74 | int adx, ady, sx, sy, err, e2, x0, x1, y0, y1, kx, ky; | |||
| 75 | #define ray_reset() \ | |||
| 76 | err = adx - ady; e2 = 0; \ | |||
| 77 | x0 = j; y0 = i; | |||
| 78 | #define ray_reset_by_stroke(stroke)adx = abs(stroke->x1 - stroke->x0); ady = abs(stroke-> y1 - stroke->y0); sx = stroke->x1 > stroke->x0 ? 1 : -1; sy = stroke->y1 > stroke->y0 ? 1 : -1; err = adx - ady; e2 = 0; x0 = stroke->x0; y0 = stroke->y0; \ | |||
| 79 | adx = abs(stroke->x1 - stroke->x0); \ | |||
| 80 | ady = abs(stroke->y1 - stroke->y0); \ | |||
| 81 | sx = stroke->x1 > stroke->x0 ? 1 : -1; \ | |||
| 82 | sy = stroke->y1 > stroke->y0 ? 1 : -1; \ | |||
| 83 | err = adx - ady; e2 = 0; \ | |||
| 84 | x0 = stroke->x0; y0 = stroke->y0; | |||
| 85 | #define ray_increment() \ | |||
| 86 | e2 = 2 * err; \ | |||
| 87 | if (e2 > -ady) \ | |||
| 88 | { \ | |||
| 89 | err -= ady; \ | |||
| 90 | x0 += sx; \ | |||
| 91 | } \ | |||
| 92 | if (e2 < adx) \ | |||
| 93 | { \ | |||
| 94 | err += adx; \ | |||
| 95 | y0 += sy; \ | |||
| 96 | } | |||
| 97 | int rdx, rdy, flag; | |||
| 98 | #define ray_emit(xx, xy, yx, yy, _for_get_d, _for_set_b, _for_get_b) \ | |||
| 99 | rdx = _for_get_d(dx_ptr, j) * (xx) + _for_get_d(dy_ptr, j) * (xy); \ | |||
| 100 | rdy = _for_get_d(dx_ptr, j) * (yx) + _for_get_d(dy_ptr, j) * (yy); \ | |||
| 101 | adx = abs(rdx); \ | |||
| 102 | ady = abs(rdy); \ | |||
| 103 | sx = rdx > 0 ? -params.direction : params.direction; \ | |||
| 104 | sy = rdy > 0 ? -params.direction : params.direction; \ | |||
| 105 | /* Bresenham's line algorithm */ \ | |||
| 106 | ray_reset(); \ | |||
| 107 | flag = 0; \ | |||
| 108 | kx = x0; \ | |||
| 109 | ky = y0; \ | |||
| 110 | for (w = 0; w < 70; w++) \ | |||
| 111 | { \ | |||
| 112 | ray_increment(); \ | |||
| 113 | if (x0 >= a->cols - 1 || x0 < 1 || y0 >= a->rows - 1 || y0 < 1) \ | |||
| 114 | break; \ | |||
| 115 | if (abs(i - y0) >= 2 || abs(j - x0) >= 2) \ | |||
| 116 | { /* ideally, I can encounter another edge directly, but in practice, we should search in a small region around it */ \ | |||
| 117 | flag = 0; \ | |||
| 118 | for (k = 0; k < 5; k++) \ | |||
| 119 | { \ | |||
| 120 | kx = x0 + dx5[k]; \ | |||
| 121 | ky = y0 + dy5[k]; \ | |||
| 122 | if (c_ptr[kx + (ky - i) * c->step]) \ | |||
| 123 | { \ | |||
| 124 | flag = 1; \ | |||
| 125 | break; \ | |||
| 126 | } \ | |||
| 127 | } \ | |||
| 128 | if (flag) \ | |||
| 129 | break; \ | |||
| 130 | } \ | |||
| 131 | } \ | |||
| 132 | if (flag && kx < a->cols - 1 && kx > 0 && ky < a->rows - 1 && ky > 0) \ | |||
| 133 | { \ | |||
| 134 | /* the opposite angle should be in d_p -/+ PI / 6 (otherwise discard), | |||
| 135 | * a faster computation should be: | |||
| 136 | * Tan(d_q - d_p) = (Tan(d_q) - Tan(d_p)) / (1 + Tan(d_q) * Tan(d_p)) | |||
| 137 | * and -1 / sqrt(3) < Tan(d_q - d_p) < 1 / sqrt(3) | |||
| 138 | * also, we needs to check the whole 3x3 neighborhood in a hope that we don't miss one or two of them */ \ | |||
| 139 | flag = 0; \ | |||
| 140 | for (k = 0; k < 9; k++) \ | |||
| 141 | { \ | |||
| 142 | int tn = _for_get_d(dy_ptr, j) * _for_get_d(dx_ptr + (ky - i + dy9[k]) * dx->step, kx + dx9[k]) - \ | |||
| 143 | _for_get_d(dx_ptr, j) * _for_get_d(dy_ptr + (ky - i + dy9[k]) * dy->step, kx + dx9[k]); \ | |||
| 144 | int td = _for_get_d(dx_ptr, j) * _for_get_d(dx_ptr + (ky - i + dy9[k]) * dx->step, kx + dx9[k]) + \ | |||
| 145 | _for_get_d(dy_ptr, j) * _for_get_d(dy_ptr + (ky - i + dy9[k]) * dy->step, kx + dx9[k]); \ | |||
| 146 | if (tn * 7 < -td * 4 && tn * 7 > td * 4) \ | |||
| 147 | { \ | |||
| 148 | flag = 1; \ | |||
| 149 | break; \ | |||
| 150 | } \ | |||
| 151 | } \ | |||
| 152 | if (flag) \ | |||
| 153 | { \ | |||
| 154 | x1 = x0; y1 = y0; \ | |||
| 155 | ray_reset(); \ | |||
| 156 | w = (int)(sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0)) + 0.5); \ | |||
| 157 | /* extend the line to be width of 1 */ \ | |||
| 158 | for (;;) \ | |||
| 159 | { \ | |||
| 160 | if (_for_get_b(b_ptr + (y0 - i) * db->step, x0) == 0 || _for_get_b(b_ptr + (y0 - i) * db->step, x0) > w) \ | |||
| 161 | _for_set_b(b_ptr + (y0 - i) * db->step, x0, w); \ | |||
| 162 | if (x0 == x1 && y0 == y1) \ | |||
| 163 | break; \ | |||
| 164 | ray_increment(); \ | |||
| 165 | } \ | |||
| 166 | ccv_swt_stroke_t stroke = { \ | |||
| 167 | .x0 = j, \ | |||
| 168 | .x1 = x1, \ | |||
| 169 | .y0 = i, \ | |||
| 170 | .y1 = y1, \ | |||
| 171 | .w = w \ | |||
| 172 | }; \ | |||
| 173 | ccv_array_push(strokes, &stroke); \ | |||
| 174 | } \ | |||
| 175 | } | |||
| 176 | #define for_block(_for_get_d, _for_set_b, _for_get_b) \ | |||
| 177 | for (i = 0; i < a->rows; i++) \ | |||
| 178 | { \ | |||
| 179 | for (j = 0; j < a->cols; j++) \ | |||
| 180 | if (c_ptr[j]) \ | |||
| 181 | { \ | |||
| 182 | ray_emit(1, 0, 0, 1, _for_get_d, _for_set_b, _for_get_b); \ | |||
| 183 | ray_emit(1, -1, 1, 1, _for_get_d, _for_set_b, _for_get_b); \ | |||
| 184 | ray_emit(1, 1, -1, 1, _for_get_d, _for_set_b, _for_get_b); \ | |||
| 185 | } \ | |||
| 186 | b_ptr += db->step; \ | |||
| 187 | c_ptr += c->step; \ | |||
| 188 | dx_ptr += dx->step; \ | |||
| 189 | dy_ptr += dy->step; \ | |||
| 190 | } \ | |||
| 191 | b_ptr = db->data.u8; \ | |||
| 192 | /* compute median width of stroke, from shortest strokes to longest */ \ | |||
| 193 | _ccv_swt_stroke_qsort((ccv_swt_stroke_t*)ccv_array_get(strokes, 0)((void*)(((char*)((strokes)->data)) + (size_t)(strokes)-> rsize * (size_t)(0))), strokes->rnum, 0); \ | |||
| 194 | for (i = 0; i < strokes->rnum; i++) \ | |||
| 195 | { \ | |||
| 196 | ccv_swt_stroke_t* stroke = (ccv_swt_stroke_t*)ccv_array_get(strokes, i)((void*)(((char*)((strokes)->data)) + (size_t)(strokes)-> rsize * (size_t)(i))); \ | |||
| 197 | ray_reset_by_stroke(stroke)adx = abs(stroke->x1 - stroke->x0); ady = abs(stroke-> y1 - stroke->y0); sx = stroke->x1 > stroke->x0 ? 1 : -1; sy = stroke->y1 > stroke->y0 ? 1 : -1; err = adx - ady; e2 = 0; x0 = stroke->x0; y0 = stroke->y0;; \ | |||
| 198 | int n = 0; \ | |||
| 199 | for (;;) \ | |||
| 200 | { \ | |||
| 201 | buf[n++] = _for_get_b(b_ptr + y0 * db->step, x0); \ | |||
| 202 | if (x0 == stroke->x1 && y0 == stroke->y1) \ | |||
| 203 | break; \ | |||
| 204 | ray_increment(); \ | |||
| 205 | } \ | |||
| 206 | int nw = _ccv_swt_median(buf, 0, n - 1); \ | |||
| 207 | if (nw != stroke->w) \ | |||
| 208 | { \ | |||
| 209 | ray_reset_by_stroke(stroke)adx = abs(stroke->x1 - stroke->x0); ady = abs(stroke-> y1 - stroke->y0); sx = stroke->x1 > stroke->x0 ? 1 : -1; sy = stroke->y1 > stroke->y0 ? 1 : -1; err = adx - ady; e2 = 0; x0 = stroke->x0; y0 = stroke->y0;; \ | |||
| 210 | for (;;) \ | |||
| 211 | { \ | |||
| 212 | _for_set_b(b_ptr + y0 * db->step, x0, nw); \ | |||
| 213 | if (x0 == stroke->x1 && y0 == stroke->y1) \ | |||
| 214 | break; \ | |||
| 215 | ray_increment(); \ | |||
| 216 | } \ | |||
| 217 | } \ | |||
| 218 | } | |||
| 219 | ccv_matrix_getter(dx->type, ccv_matrix_setter_getter, db->type, for_block){ switch (((dx->type) & 0xFF000)) { case CCV_32S: { { switch (((db->type) & 0xFF000)) { case CCV_32S: { for_block( _ccv_get_32s_value, _ccv_set_32s_value, _ccv_get_32s_value); break ; } case CCV_32F: { for_block(_ccv_get_32s_value, _ccv_set_32f_value , _ccv_get_32f_value); break; } case CCV_64S: { for_block(_ccv_get_32s_value , _ccv_set_64s_value, _ccv_get_64s_value); break; } case CCV_64F : { for_block(_ccv_get_32s_value, _ccv_set_64f_value, _ccv_get_64f_value ); break; } default: { for_block(_ccv_get_32s_value, _ccv_set_8u_value , _ccv_get_8u_value); } } }; break; } case CCV_32F: { { switch (((db->type) & 0xFF000)) { case CCV_32S: { for_block( _ccv_get_32f_value, _ccv_set_32s_value, _ccv_get_32s_value); break ; } case CCV_32F: { for_block(_ccv_get_32f_value, _ccv_set_32f_value , _ccv_get_32f_value); break; } case CCV_64S: { for_block(_ccv_get_32f_value , _ccv_set_64s_value, _ccv_get_64s_value); break; } case CCV_64F : { for_block(_ccv_get_32f_value, _ccv_set_64f_value, _ccv_get_64f_value ); break; } default: { for_block(_ccv_get_32f_value, _ccv_set_8u_value , _ccv_get_8u_value); } } }; break; } case CCV_64S: { { switch (((db->type) & 0xFF000)) { case CCV_32S: { for_block( _ccv_get_64s_value, _ccv_set_32s_value, _ccv_get_32s_value); break ; } case CCV_32F: { for_block(_ccv_get_64s_value, _ccv_set_32f_value , _ccv_get_32f_value); break; } case CCV_64S: { for_block(_ccv_get_64s_value , _ccv_set_64s_value, _ccv_get_64s_value); break; } case CCV_64F : { for_block(_ccv_get_64s_value, _ccv_set_64f_value, _ccv_get_64f_value ); break; } default: { for_block(_ccv_get_64s_value, _ccv_set_8u_value , _ccv_get_8u_value); } } }; break; } case CCV_64F: { { switch (((db->type) & 0xFF000)) { case CCV_32S: { for_block( _ccv_get_64f_value, _ccv_set_32s_value, _ccv_get_32s_value); break ; } case CCV_32F: { for_block(_ccv_get_64f_value, _ccv_set_32f_value , _ccv_get_32f_value); break; } case CCV_64S: { for_block(_ccv_get_64f_value , _ccv_set_64s_value, _ccv_get_64s_value); break; } case CCV_64F : { for_block(_ccv_get_64f_value, _ccv_set_64f_value, _ccv_get_64f_value ); break; } default: { for_block(_ccv_get_64f_value, _ccv_set_8u_value , _ccv_get_8u_value); } } }; break; } default: { { switch ((( db->type) & 0xFF000)) { case CCV_32S: { for_block(_ccv_get_8u_value , _ccv_set_32s_value, _ccv_get_32s_value); break; } case CCV_32F : { for_block(_ccv_get_8u_value, _ccv_set_32f_value, _ccv_get_32f_value ); break; } case CCV_64S: { for_block(_ccv_get_8u_value, _ccv_set_64s_value , _ccv_get_64s_value); break; } case CCV_64F: { for_block(_ccv_get_8u_value , _ccv_set_64f_value, _ccv_get_64f_value); break; } default: { for_block(_ccv_get_8u_value, _ccv_set_8u_value, _ccv_get_8u_value ); } } }; } } }; | |||
| 220 | #undef for_block | |||
| 221 | #undef ray_emit | |||
| 222 | #undef ray_reset | |||
| 223 | #undef ray_increment | |||
| 224 | ccv_array_free(strokes); | |||
| 225 | if (c != _c) | |||
| 226 | ccv_matrix_free(c); | |||
| 227 | if (dx != _dx) | |||
| 228 | ccv_matrix_free(dx); | |||
| 229 | if (dy != _dy) | |||
| 230 | ccv_matrix_free(dy); | |||
| 231 | } | |||
| 232 | ||||
| 233 | void ccv_swt(ccv_dense_matrix_t* a, ccv_dense_matrix_t** b, int type, ccv_swt_param_t params) | |||
| 234 | { | |||
| 235 | _ccv_swt(a, b, type, params, 0, 0, 0); | |||
| 236 | } | |||
| 237 | ||||
| 238 | static ccv_array_t* _ccv_swt_connected_component(ccv_dense_matrix_t* a, int ratio, int min_height, int max_height, int min_area) | |||
| 239 | { | |||
| 240 | int i, j, k; | |||
| 241 | int* a_ptr = a->data.i32; | |||
| 242 | int dx8[] = {-1, 1, -1, 0, 1, -1, 0, 1}; | |||
| 243 | int dy8[] = {0, 0, -1, -1, -1, 1, 1, 1}; | |||
| 244 | int* marker = (int*)ccmallocmalloc(sizeof(int) * a->rows * a->cols); | |||
| 245 | memset(marker, 0, sizeof(int) * a->rows * a->cols); | |||
| 246 | int* m_ptr = marker; | |||
| 247 | ccv_point_t* buffer = (ccv_point_t*)ccmallocmalloc(sizeof(ccv_point_t) * a->rows * a->cols); | |||
| 248 | ccv_array_t* contours = ccv_array_new(sizeof(ccv_contour_t*), 5, 0); | |||
| 249 | for (i = 0; i < a->rows; i++) | |||
| 250 | { | |||
| 251 | for (j = 0; j < a->cols; j++) | |||
| 252 | if (a_ptr[j] != 0 && !m_ptr[j]) | |||
| 253 | { | |||
| 254 | m_ptr[j] = 1; | |||
| 255 | ccv_contour_t* contour = ccv_contour_new(1); | |||
| 256 | ccv_point_t* closed = buffer; | |||
| 257 | closed->x = j; | |||
| 258 | closed->y = i; | |||
| 259 | ccv_point_t* open = buffer + 1; | |||
| 260 | for (; closed < open; closed++) | |||
| 261 | { | |||
| 262 | ccv_contour_push(contour, *closed); | |||
| 263 | double w = a_ptr[closed->x + (closed->y - i) * a->cols]; | |||
| 264 | for (k = 0; k < 8; k++) | |||
| 265 | { | |||
| 266 | int nx = closed->x + dx8[k]; | |||
| 267 | int ny = closed->y + dy8[k]; | |||
| 268 | if (nx >= 0 && nx < a->cols && ny >= 0 && ny < a->rows && | |||
| 269 | a_ptr[nx + (ny - i) * a->cols] != 0 && | |||
| 270 | !m_ptr[nx + (ny - i) * a->cols] && | |||
| 271 | (a_ptr[nx + (ny - i) * a->cols] <= ratio * w && a_ptr[nx + (ny - i) * a->cols] * ratio >= w)) | |||
| 272 | { | |||
| 273 | m_ptr[nx + (ny - i) * a->cols] = 1; | |||
| 274 | // compute new average w | |||
| 275 | w = (w * (int)(open - closed + 1) + a_ptr[nx + (ny - i) * a->cols]) / (double)(open - closed + 2); | |||
| 276 | open->x = nx; | |||
| 277 | open->y = ny; | |||
| 278 | open++; | |||
| 279 | } | |||
| 280 | } | |||
| 281 | } | |||
| 282 | if (contour->rect.height < min_height || contour->rect.height > max_height || contour->size < min_area) | |||
| 283 | ccv_contour_free(contour); | |||
| 284 | else | |||
| 285 | ccv_array_push(contours, &contour); | |||
| 286 | } | |||
| 287 | a_ptr += a->cols; | |||
| 288 | m_ptr += a->cols; | |||
| 289 | } | |||
| 290 | ccfreefree(marker); | |||
| 291 | ccfreefree(buffer); | |||
| 292 | return contours; | |||
| 293 | } | |||
| 294 | ||||
| 295 | typedef struct { | |||
| 296 | ccv_rect_t rect; | |||
| 297 | ccv_point_t center; | |||
| 298 | int thickness; | |||
| 299 | int intensity; | |||
| 300 | double std; | |||
| 301 | double mean; | |||
| 302 | ccv_contour_t* contour; | |||
| 303 | } ccv_letter_t; | |||
| 304 | ||||
| 305 | static ccv_array_t* _ccv_swt_connected_letters(ccv_dense_matrix_t* a, ccv_dense_matrix_t* swt, ccv_swt_param_t params) | |||
| 306 | { | |||
| 307 | ccv_array_t* contours = _ccv_swt_connected_component(swt, 3, params.min_height, params.max_height, params.min_area); | |||
| 308 | ccv_array_t* letters = ccv_array_new(sizeof(ccv_letter_t), 5, 0); | |||
| 309 | int i, j, x, y; | |||
| 310 | // merge contours that inside other contours | |||
| 311 | int* buffer = (int*)ccmallocmalloc(sizeof(int) * swt->rows * swt->cols); | |||
| 312 | double aspect_ratio_inv = 1.0 / params.aspect_ratio; | |||
| 313 | for (i = 0; i < contours->rnum; i++) | |||
| 314 | { | |||
| 315 | ccv_contour_t* contour = *(ccv_contour_t**)ccv_array_get(contours, i)((void*)(((char*)((contours)->data)) + (size_t)(contours)-> rsize * (size_t)(i))); | |||
| 316 | assert(contour->rect.height <= params.max_height && contour->rect.height >= params.min_height)((void) sizeof ((contour->rect.height <= params.max_height && contour->rect.height >= params.min_height) ? 1 : 0), __extension__ ({ if (contour->rect.height <= params .max_height && contour->rect.height >= params.min_height ) ; else __assert_fail ("contour->rect.height <= params.max_height && contour->rect.height >= params.min_height" , "ccv_swt.c", 316, __extension__ __PRETTY_FUNCTION__); })); | |||
| 317 | double ratio = (double)contour->rect.width / (double)contour->rect.height; | |||
| 318 | if (ratio < aspect_ratio_inv || ratio > params.aspect_ratio) | |||
| 319 | { | |||
| 320 | ccv_contour_free(contour); | |||
| 321 | continue; | |||
| 322 | } | |||
| 323 | double xc = (double)contour->m10 / contour->size; | |||
| 324 | double yc = (double)contour->m01 / contour->size; | |||
| 325 | double af = (double)contour->m20 / contour->size - xc * xc; | |||
| 326 | double bf = 2 * ((double)contour->m11 / contour->size - xc * yc); | |||
| 327 | double cf = (double)contour->m02 / contour->size - yc * yc; | |||
| 328 | double delta = sqrt(bf * bf + (af - cf) * (af - cf)); | |||
| 329 | ratio = sqrt((af + cf + delta) / (af + cf - delta)); | |||
| 330 | if (ratio < aspect_ratio_inv || ratio > params.aspect_ratio) | |||
| 331 | { | |||
| 332 | ccv_contour_free(contour); | |||
| 333 | continue; | |||
| 334 | } | |||
| 335 | double mean = 0; | |||
| 336 | for (j = 0; j < contour->size; j++) | |||
| 337 | { | |||
| 338 | ccv_point_t* point = (ccv_point_t*)ccv_array_get(contour->set, j)((void*)(((char*)((contour->set)->data)) + (size_t)(contour ->set)->rsize * (size_t)(j))); | |||
| 339 | mean += buffer[j] = swt->data.i32[point->x + point->y * swt->cols]; | |||
| 340 | } | |||
| 341 | mean = mean / contour->size; | |||
| 342 | double variance = 0; | |||
| 343 | for (j = 0; j < contour->size; j++) | |||
| 344 | variance += (mean - buffer[j]) * (mean - buffer[j]); | |||
| 345 | variance = variance / contour->size; | |||
| 346 | ccv_letter_t letter; | |||
| 347 | letter.std = sqrt(variance); | |||
| 348 | letter.mean = mean; | |||
| 349 | letter.thickness = _ccv_swt_median(buffer, 0, contour->size - 1); | |||
| 350 | letter.rect = contour->rect; | |||
| 351 | letter.center.x = letter.rect.x + letter.rect.width / 2; | |||
| 352 | letter.center.y = letter.rect.y + letter.rect.height / 2; | |||
| 353 | letter.intensity = 0; | |||
| 354 | letter.contour = contour; | |||
| 355 | ccv_array_push(letters, &letter); | |||
| 356 | } | |||
| 357 | ccv_array_free(contours); | |||
| 358 | memset(buffer, 0, sizeof(int) * swt->rows * swt->cols); | |||
| 359 | ccv_array_t* new_letters = ccv_array_new(sizeof(ccv_letter_t), 5, 0); | |||
| 360 | for (i = 0; i < letters->rnum; i++) | |||
| 361 | { | |||
| 362 | ccv_letter_t* letter = (ccv_letter_t*)ccv_array_get(letters, i)((void*)(((char*)((letters)->data)) + (size_t)(letters)-> rsize * (size_t)(i))); | |||
| 363 | for (j = 0; j < letter->contour->size; j++) | |||
| 364 | { | |||
| 365 | ccv_point_t* point = (ccv_point_t*)ccv_array_get(letter->contour->set, j)((void*)(((char*)((letter->contour->set)->data)) + ( size_t)(letter->contour->set)->rsize * (size_t)(j))); | |||
| 366 | buffer[point->x + point->y * swt->cols] = i + 1; | |||
| 367 | } | |||
| 368 | } | |||
| 369 | // filter out letters that intersects more than 2 other letters | |||
| 370 | int* another = params.letter_occlude_thresh ? (int*)alloca(sizeof(int) * params.letter_occlude_thresh)__builtin_alloca (sizeof(int) * params.letter_occlude_thresh) : 0; | |||
| 371 | for (i = 0; i < letters->rnum; i++) | |||
| 372 | { | |||
| 373 | ccv_letter_t* letter = (ccv_letter_t*)ccv_array_get(letters, i)((void*)(((char*)((letters)->data)) + (size_t)(letters)-> rsize * (size_t)(i))); | |||
| 374 | if (letter->std > letter->mean * params.std_ratio) | |||
| 375 | { | |||
| 376 | ccv_contour_free(letter->contour); | |||
| 377 | continue; | |||
| 378 | } | |||
| 379 | int more = 0; | |||
| 380 | if (another) | |||
| 381 | { | |||
| 382 | // one letter cannot occlude with more than params.letter_occlude_thresh other letters | |||
| 383 | memset(another, 0, sizeof(int) * params.letter_occlude_thresh); | |||
| 384 | for (x = letter->rect.x; x < letter->rect.x + letter->rect.width; x++) | |||
| 385 | { | |||
| 386 | for (y = letter->rect.y; y < letter->rect.y + letter->rect.height; y++) | |||
| 387 | { | |||
| 388 | int group = buffer[x + swt->cols * y]; | |||
| 389 | if (group && group != i + 1) | |||
| 390 | { | |||
| 391 | more = 1; | |||
| 392 | for (j = 0; j < params.letter_occlude_thresh; j++) | |||
| 393 | if (!another[j] || another[j] == group) | |||
| 394 | { | |||
| 395 | another[j] = group; | |||
| 396 | more = 0; | |||
| 397 | break; | |||
| 398 | } | |||
| 399 | if (more) | |||
| 400 | break; | |||
| 401 | } | |||
| 402 | } | |||
| 403 | if (more) | |||
| 404 | break; | |||
| 405 | } | |||
| 406 | } | |||
| 407 | if (more) | |||
| 408 | { | |||
| 409 | ccv_contour_free(letter->contour); | |||
| 410 | continue; | |||
| 411 | } | |||
| 412 | for (j = 0; j < letter->contour->set->rnum; j++) | |||
| 413 | { | |||
| 414 | ccv_point_t* point = (ccv_point_t*)ccv_array_get(letter->contour->set, j)((void*)(((char*)((letter->contour->set)->data)) + ( size_t)(letter->contour->set)->rsize * (size_t)(j))); | |||
| 415 | letter->intensity += a->data.u8[point->x + point->y * a->step]; | |||
| 416 | } | |||
| 417 | letter->intensity /= letter->contour->size; | |||
| 418 | ccv_contour_free(letter->contour); | |||
| 419 | letter->contour = 0; | |||
| 420 | ccv_array_push(new_letters, letter); | |||
| 421 | } | |||
| 422 | ccv_array_free(letters); | |||
| 423 | ccfreefree(buffer); | |||
| 424 | return new_letters; | |||
| 425 | } | |||
| 426 | ||||
| 427 | typedef struct { | |||
| 428 | ccv_letter_t* left; | |||
| 429 | ccv_letter_t* right; | |||
| 430 | int dx; | |||
| 431 | int dy; | |||
| 432 | } ccv_letter_pair_t; | |||
| 433 | ||||
| 434 | typedef struct { | |||
| 435 | ccv_rect_t rect; | |||
| 436 | int neighbors; | |||
| 437 | ccv_letter_t** letters; | |||
| 438 | } ccv_textline_t; | |||
| 439 | ||||
| 440 | static int _ccv_in_textline(const void* a, const void* b, void* data) | |||
| 441 | { | |||
| 442 | ccv_letter_pair_t* pair1 = (ccv_letter_pair_t*)a; | |||
| 443 | ccv_letter_pair_t* pair2 = (ccv_letter_pair_t*)b; | |||
| 444 | if (pair1->left == pair2->left || pair1->right == pair2->right) | |||
| 445 | { | |||
| 446 | int tn = pair1->dy * pair2->dx - pair1->dx * pair2->dy; | |||
| 447 | int td = pair1->dx * pair2->dx + pair1->dy * pair2->dy; | |||
| 448 | // share the same end, opposite direction | |||
| 449 | if (tn * 7 < -td * 4 && tn * 7 > td * 4) | |||
| 450 | return 1; | |||
| 451 | } else if (pair1->left == pair2->right || pair1->right == pair2->left) { | |||
| 452 | int tn = pair1->dy * pair2->dx - pair1->dx * pair2->dy; | |||
| 453 | int td = pair1->dx * pair2->dx + pair1->dy * pair2->dy; | |||
| 454 | // share the other end, same direction | |||
| 455 | if (tn * 7 < td * 4 && tn * 7 > -td * 4) | |||
| 456 | return 1; | |||
| 457 | } | |||
| 458 | return 0; | |||
| 459 | } | |||
| 460 | ||||
| 461 | static void _ccv_swt_add_letter(ccv_textline_t* textline, ccv_letter_t* letter) | |||
| 462 | { | |||
| 463 | if (textline->neighbors == 0) | |||
| ||||
| 464 | { | |||
| 465 | textline->rect = letter->rect; | |||
| 466 | textline->neighbors = 1; | |||
| 467 | textline->letters = (ccv_letter_t**)ccmallocmalloc(sizeof(ccv_letter_t*) * textline->neighbors); | |||
| 468 | textline->letters[0] = letter; | |||
| 469 | } else { | |||
| 470 | int i, flag = 0; | |||
| 471 | for (i = 0; i < textline->neighbors; i++) | |||
| 472 | if (textline->letters[i] == letter) | |||
| 473 | { | |||
| 474 | flag = 1; | |||
| 475 | break; | |||
| 476 | } | |||
| 477 | if (flag) | |||
| 478 | return; | |||
| 479 | if (letter->rect.x < textline->rect.x) | |||
| 480 | { | |||
| 481 | textline->rect.width += textline->rect.x - letter->rect.x; | |||
| 482 | textline->rect.x = letter->rect.x; | |||
| 483 | } | |||
| 484 | if (letter->rect.x + letter->rect.width > textline->rect.x + textline->rect.width) | |||
| 485 | textline->rect.width = letter->rect.x + letter->rect.width - textline->rect.x; | |||
| 486 | if (letter->rect.y < textline->rect.y) | |||
| 487 | { | |||
| 488 | textline->rect.height += textline->rect.y - letter->rect.y; | |||
| 489 | textline->rect.y = letter->rect.y; | |||
| 490 | } | |||
| 491 | if (letter->rect.y + letter->rect.height > textline->rect.y + textline->rect.height) | |||
| 492 | textline->rect.height = letter->rect.y + letter->rect.height - textline->rect.y; | |||
| 493 | textline->neighbors++; | |||
| 494 | textline->letters = (ccv_letter_t**)ccreallocrealloc(textline->letters, sizeof(ccv_letter_t*) * textline->neighbors); | |||
| 495 | textline->letters[textline->neighbors - 1] = letter; | |||
| 496 | } | |||
| 497 | } | |||
| 498 | ||||
| 499 | static ccv_array_t* _ccv_swt_merge_textline(ccv_array_t* letters, ccv_swt_param_t params) | |||
| 500 | { | |||
| 501 | int i, j; | |||
| 502 | ccv_array_t* pairs = ccv_array_new(sizeof(ccv_letter_pair_t), letters->rnum, 0); | |||
| 503 | double thickness_ratio_inv = 1.0 / params.thickness_ratio; | |||
| 504 | double height_ratio_inv = 1.0 / params.height_ratio; | |||
| 505 | for (i = 0; i < letters->rnum - 1; i++) | |||
| 506 | { | |||
| 507 | ccv_letter_t* li = (ccv_letter_t*)ccv_array_get(letters, i)((void*)(((char*)((letters)->data)) + (size_t)(letters)-> rsize * (size_t)(i))); | |||
| 508 | for (j = i + 1; j < letters->rnum; j++) | |||
| 509 | { | |||
| 510 | ccv_letter_t* lj = (ccv_letter_t*)ccv_array_get(letters, j)((void*)(((char*)((letters)->data)) + (size_t)(letters)-> rsize * (size_t)(j))); | |||
| 511 | double ratio = (double)li->thickness / lj->thickness; | |||
| 512 | if (ratio > params.thickness_ratio || ratio < thickness_ratio_inv) | |||
| 513 | continue; | |||
| 514 | ratio = (double)li->rect.height / lj->rect.height; | |||
| 515 | if (ratio > params.height_ratio || ratio < height_ratio_inv) | |||
| 516 | continue; | |||
| 517 | if (abs(li->intensity - lj->intensity) > params.intensity_thresh) | |||
| 518 | continue; | |||
| 519 | int dx = li->rect.x - lj->rect.x + (li->rect.width - lj->rect.width) / 2; | |||
| 520 | int dy = li->rect.y - lj->rect.y + (li->rect.height - lj->rect.height) / 2; | |||
| 521 | if (abs(dx) > params.distance_ratio * ccv_max(li->rect.width, lj->rect.width)({ typeof (li->rect.width) _a = (li->rect.width); typeof (lj->rect.width) _b = (lj->rect.width); (_a > _b) ? _a : _b; })) | |||
| 522 | continue; | |||
| 523 | int oy = ccv_min(li->rect.y + li->rect.height, lj->rect.y + lj->rect.height)({ typeof (li->rect.y + li->rect.height) _a = (li->rect .y + li->rect.height); typeof (lj->rect.y + lj->rect .height) _b = (lj->rect.y + lj->rect.height); (_a < _b ) ? _a : _b; }) - ccv_max(li->rect.y, lj->rect.y)({ typeof (li->rect.y) _a = (li->rect.y); typeof (lj-> rect.y) _b = (lj->rect.y); (_a > _b) ? _a : _b; }); | |||
| 524 | if (oy * params.intersect_ratio < ccv_min(li->rect.height, lj->rect.height)({ typeof (li->rect.height) _a = (li->rect.height); typeof (lj->rect.height) _b = (lj->rect.height); (_a < _b) ? _a : _b; })) | |||
| 525 | continue; | |||
| 526 | ccv_letter_pair_t pair = { .left = li, .right = lj, .dx = dx, .dy = dy }; | |||
| 527 | ccv_array_push(pairs, &pair); | |||
| 528 | } | |||
| 529 | } | |||
| 530 | ccv_array_t* idx = 0; | |||
| 531 | int nchains = ccv_array_group(pairs, &idx, _ccv_in_textline, 0); | |||
| 532 | ccv_textline_t* chain = (ccv_textline_t*)ccmallocmalloc(nchains * sizeof(ccv_textline_t)); | |||
| 533 | for (i = 0; i < nchains; i++) | |||
| 534 | chain[i].neighbors = 0; | |||
| 535 | for (i = 0; i < pairs->rnum; i++) | |||
| 536 | { | |||
| 537 | j = *(int*)ccv_array_get(idx, i)((void*)(((char*)((idx)->data)) + (size_t)(idx)->rsize * (size_t)(i))); | |||
| 538 | _ccv_swt_add_letter(chain + j,((ccv_letter_pair_t*)ccv_array_get(pairs, i)((void*)(((char*)((pairs)->data)) + (size_t)(pairs)->rsize * (size_t)(i))))->left); | |||
| 539 | _ccv_swt_add_letter(chain + j, ((ccv_letter_pair_t*)ccv_array_get(pairs, i)((void*)(((char*)((pairs)->data)) + (size_t)(pairs)->rsize * (size_t)(i))))->right); | |||
| 540 | } | |||
| 541 | ccv_array_free(idx); | |||
| 542 | ccv_array_free(pairs); | |||
| 543 | ccv_array_t* regions = ccv_array_new(sizeof(ccv_textline_t), 5, 0); | |||
| 544 | for (i = 0; i < nchains; i++) | |||
| 545 | if (chain[i].neighbors >= params.letter_thresh && chain[i].rect.width > chain[i].rect.height * params.elongate_ratio) | |||
| 546 | ccv_array_push(regions, chain + i); | |||
| 547 | else if (chain[i].neighbors > 0) | |||
| 548 | ccfreefree(chain[i].letters); | |||
| 549 | ccfreefree(chain); | |||
| 550 | return regions; | |||
| 551 | } | |||
| 552 | ||||
| 553 | #define less_than(a, b, aux) ((a)->center.x < (b)->center.x) | |||
| 554 | static CCV_IMPLEMENT_QSORT(_ccv_sort_letters, ccv_letter_t*, less_than)void _ccv_sort_letters(ccv_letter_t* *array, size_t total, int aux) { int isort_thresh = 7; ccv_letter_t* t; int sp = 0; struct { ccv_letter_t* *lb; ccv_letter_t* *ub; } stack[48]; if( total <= 1 ) return; stack[0].lb = array; stack[0].ub = array + (total - 1); while( sp >= 0 ) { ccv_letter_t** left = stack [sp].lb; ccv_letter_t** right = stack[sp--].ub; for(;;) { int i, n = (int)(right - left) + 1, m; ccv_letter_t** ptr; ccv_letter_t ** ptr2; if( n <= isort_thresh ) { insert_sort: for( ptr = left + 1; ptr <= right; ptr++ ) { for( ptr2 = ptr; ptr2 > left && less_than(ptr2[0],ptr2[-1], aux); ptr2--) (( (t)) = ((ptr2[0])), ((ptr2[0])) = ((ptr2[-1])), ((ptr2[-1])) = ((t))); } break; } else { ccv_letter_t** left0; ccv_letter_t ** left1; ccv_letter_t** right0; ccv_letter_t** right1; ccv_letter_t ** pivot; ccv_letter_t** a; ccv_letter_t** b; ccv_letter_t** c ; int swap_cnt = 0; left0 = left; right0 = right; pivot = left + (n/2); if( n > 40 ) { int d = n / 8; a = left, b = left + d, c = left + 2*d; left = less_than(*a, *b, aux) ? (less_than (*b, *c, aux) ? b : (less_than(*a, *c, aux) ? c : a)) : (less_than (*c, *b, aux) ? b : (less_than(*a, *c, aux) ? a : c)); a = pivot - d, b = pivot, c = pivot + d; pivot = less_than(*a, *b, aux ) ? (less_than(*b, *c, aux) ? b : (less_than(*a, *c, aux) ? c : a)) : (less_than(*c, *b, aux) ? b : (less_than(*a, *c, aux ) ? a : c)); a = right - 2*d, b = right - d, c = right; right = less_than(*a, *b, aux) ? (less_than(*b, *c, aux) ? b : (less_than (*a, *c, aux) ? c : a)) : (less_than(*c, *b, aux) ? b : (less_than (*a, *c, aux) ? a : c)); } a = left, b = pivot, c = right; pivot = less_than(*a, *b, aux) ? (less_than(*b, *c, aux) ? b : (less_than (*a, *c, aux) ? c : a)) : (less_than(*c, *b, aux) ? b : (less_than (*a, *c, aux) ? a : c)); if( pivot != left0 ) { (((t)) = ((*pivot )), ((*pivot)) = ((*left0)), ((*left0)) = ((t))); pivot = left0 ; } left = left1 = left0 + 1; right = right1 = right0; for(;; ) { while( left <= right && !less_than(*pivot, *left , aux) ) { if( !less_than(*left, *pivot, aux) ) { if( left > left1 ) (((t)) = ((*left1)), ((*left1)) = ((*left)), ((*left )) = ((t))); swap_cnt = 1; left1++; } left++; } while( left <= right && !less_than(*right, *pivot, aux) ) { if( !less_than (*pivot, *right, aux) ) { if( right < right1 ) (((t)) = (( *right1)), ((*right1)) = ((*right)), ((*right)) = ((t))); swap_cnt = 1; right1--; } right--; } if( left > right ) break; ((( t)) = ((*left)), ((*left)) = ((*right)), ((*right)) = ((t))); swap_cnt = 1; left++; right--; } if( swap_cnt == 0 ) { left = left0, right = right0; goto insert_sort; } n = ({ typeof ((int )(left1 - left0)) _a = ((int)(left1 - left0)); typeof ((int)( left - left1)) _b = ((int)(left - left1)); (_a < _b) ? _a : _b; }); for( i = 0; i < n; i++ ) (((t)) = ((left0[i])), ( (left0[i])) = ((left[i-n])), ((left[i-n])) = ((t))); n = ({ typeof ((int)(right0 - right1)) _a = ((int)(right0 - right1)); typeof ((int)(right1 - right)) _b = ((int)(right1 - right)); (_a < _b) ? _a : _b; }); for( i = 0; i < n; i++ ) (((t)) = ((left [i])), ((left[i])) = ((right0[i-n+1])), ((right0[i-n+1])) = ( (t))); n = (int)(left - left1); m = (int)(right1 - right); if ( n > 1 ) { if( m > 1 ) { if( n > m ) { stack[++sp]. lb = left0; stack[sp].ub = left0 + n - 1; left = right0 - m + 1, right = right0; } else { stack[++sp].lb = right0 - m + 1; stack[sp].ub = right0; left = left0, right = left0 + n - 1; } } else left = left0, right = left0 + n - 1; } else if( m > 1 ) left = right0 - m + 1, right = right0; else break; } } } } | |||
| 555 | #undef less_than | |||
| 556 | ||||
| 557 | static ccv_array_t* _ccv_swt_break_words(ccv_array_t* textline, ccv_swt_param_t params) | |||
| 558 | { | |||
| 559 | int i, j, n = 0; | |||
| 560 | for (i = 0; i < textline->rnum; i++) | |||
| 561 | { | |||
| 562 | ccv_textline_t* t = (ccv_textline_t*)ccv_array_get(textline, i)((void*)(((char*)((textline)->data)) + (size_t)(textline)-> rsize * (size_t)(i))); | |||
| 563 | if (t->neighbors - 1 > n) | |||
| 564 | n = t->neighbors - 1; | |||
| 565 | } | |||
| 566 | assert(n > 0)((void) sizeof ((n > 0) ? 1 : 0), __extension__ ({ if (n > 0) ; else __assert_fail ("n > 0", "ccv_swt.c", 566, __extension__ __PRETTY_FUNCTION__); })); | |||
| 567 | int* buffer = (int*)alloca(n * sizeof(int))__builtin_alloca (n * sizeof(int)); | |||
| 568 | ccv_array_t* words = ccv_array_new(sizeof(ccv_rect_t), textline->rnum, 0); | |||
| 569 | for (i = 0; i < textline->rnum; i++) | |||
| 570 | { | |||
| 571 | ccv_textline_t* t = (ccv_textline_t*)ccv_array_get(textline, i)((void*)(((char*)((textline)->data)) + (size_t)(textline)-> rsize * (size_t)(i))); | |||
| 572 | _ccv_sort_letters(t->letters, t->neighbors, 0); | |||
| 573 | int range = 0; | |||
| 574 | double mean = 0; | |||
| 575 | for (j = 0; j < t->neighbors - 1; j++) | |||
| 576 | { | |||
| 577 | buffer[j] = ccv_max(0, t->letters[j + 1]->rect.x - (t->letters[j]->rect.x + t->letters[j]->rect.width))({ typeof (0) _a = (0); typeof (t->letters[j + 1]->rect .x - (t->letters[j]->rect.x + t->letters[j]->rect .width)) _b = (t->letters[j + 1]->rect.x - (t->letters [j]->rect.x + t->letters[j]->rect.width)); (_a > _b ) ? _a : _b; }); | |||
| 578 | if (buffer[j] >= range) | |||
| 579 | range = buffer[j] + 1; | |||
| 580 | mean += buffer[j]; | |||
| 581 | } | |||
| 582 | ccv_dense_matrix_t otsu = ccv_dense_matrix(1, t->neighbors - 1, CCV_32S | CCV_C1, buffer, 0); | |||
| 583 | double var; | |||
| 584 | int threshold = ccv_otsu(&otsu, &var, range); | |||
| 585 | mean = mean / (t->neighbors - 1); | |||
| 586 | if (sqrt(var) > mean * params.breakdown_ratio) | |||
| 587 | { | |||
| 588 | ccv_textline_t nt = { .neighbors = 0, .letters = 0 }; | |||
| 589 | _ccv_swt_add_letter(&nt, t->letters[0]); | |||
| 590 | for (j = 0; j < t->neighbors - 1; j++) | |||
| 591 | { | |||
| 592 | if (buffer[j] > threshold) | |||
| 593 | { | |||
| 594 | ccv_array_push(words, &nt.rect); | |||
| 595 | if (nt.letters) | |||
| 596 | ccfreefree(nt.letters); | |||
| 597 | nt.letters = 0; | |||
| 598 | nt.neighbors = 0; | |||
| 599 | } | |||
| 600 | _ccv_swt_add_letter(&nt, t->letters[j + 1]); | |||
| 601 | } | |||
| 602 | ccv_array_push(words, &nt.rect); | |||
| 603 | if (nt.letters) | |||
| 604 | ccfreefree(nt.letters); | |||
| 605 | } else { | |||
| 606 | ccv_array_push(words, &(t->rect)); | |||
| 607 | } | |||
| 608 | } | |||
| 609 | return words; | |||
| 610 | } | |||
| 611 | ||||
| 612 | static int _ccv_is_same_textline(const void* a, const void* b, void* data) | |||
| 613 | { | |||
| 614 | ccv_textline_t* t1 = (ccv_textline_t*)a; | |||
| 615 | ccv_textline_t* t2 = (ccv_textline_t*)b; | |||
| 616 | int width = ccv_min(t1->rect.x + t1->rect.width, t2->rect.x + t2->rect.width)({ typeof (t1->rect.x + t1->rect.width) _a = (t1->rect .x + t1->rect.width); typeof (t2->rect.x + t2->rect. width) _b = (t2->rect.x + t2->rect.width); (_a < _b) ? _a : _b; }) - ccv_max(t1->rect.x, t2->rect.x)({ typeof (t1->rect.x) _a = (t1->rect.x); typeof (t2-> rect.x) _b = (t2->rect.x); (_a > _b) ? _a : _b; }); | |||
| 617 | int height = ccv_min(t1->rect.y + t1->rect.height, t2->rect.y + t2->rect.height)({ typeof (t1->rect.y + t1->rect.height) _a = (t1->rect .y + t1->rect.height); typeof (t2->rect.y + t2->rect .height) _b = (t2->rect.y + t2->rect.height); (_a < _b ) ? _a : _b; }) - ccv_max(t1->rect.y, t2->rect.y)({ typeof (t1->rect.y) _a = (t1->rect.y); typeof (t2-> rect.y) _b = (t2->rect.y); (_a > _b) ? _a : _b; }); | |||
| 618 | /* overlapped 10% */ | |||
| 619 | double* thresh = (double*)data; | |||
| 620 | return (width > 0 && height > 0 && | |||
| 621 | width * height > thresh[0] * ccv_max(t1->rect.width * t1->rect.height, t2->rect.width * t2->rect.height)({ typeof (t1->rect.width * t1->rect.height) _a = (t1-> rect.width * t1->rect.height); typeof (t2->rect.width * t2->rect.height) _b = (t2->rect.width * t2->rect.height ); (_a > _b) ? _a : _b; }) && | |||
| 622 | width * height > thresh[1] * ccv_min(t1->rect.width * t1->rect.height, t2->rect.width * t2->rect.height)({ typeof (t1->rect.width * t1->rect.height) _a = (t1-> rect.width * t1->rect.height); typeof (t2->rect.width * t2->rect.height) _b = (t2->rect.width * t2->rect.height ); (_a < _b) ? _a : _b; })); | |||
| 623 | } | |||
| 624 | ||||
| 625 | ccv_array_t* ccv_swt_detect_words(ccv_dense_matrix_t* a, ccv_swt_param_t params) | |||
| 626 | { | |||
| 627 | int hr = a->rows * 2 / (params.min_height + params.max_height); | |||
| 628 | int wr = a->cols * 2 / (params.min_height + params.max_height); | |||
| 629 | double scale = pow(2., 1. / (params.interval + 1.)); | |||
| 630 | int next = params.interval + 1; | |||
| 631 | int scale_upto = params.scale_invariant ? (int)(log((double)ccv_min(hr, wr)({ typeof (hr) _a = (hr); typeof (wr) _b = (wr); (_a < _b) ? _a : _b; })) / log(scale)) : 1; | |||
| ||||
| 632 | int i, k; | |||
| 633 | ccv_array_t* all_words = params.scale_invariant
| |||
| 634 | ccv_dense_matrix_t* phx = a; | |||
| 635 | ccv_dense_matrix_t* pyr = a; | |||
| 636 | double cscale = 1.0; | |||
| 637 | for (k = 0; k < scale_upto; k++) | |||
| 638 | { | |||
| 639 | // create down-sampled image on-demand because swt itself is very memory intensive | |||
| 640 | if (k % next) | |||
| 641 | { | |||
| 642 | pyr = 0; | |||
| 643 | int j = k % next; | |||
| 644 | ccv_resample(phx, &pyr, 0, (int)(phx->rows / pow(scale, j)), (int)(phx->cols / pow(scale, j)), CCV_INTER_AREA); | |||
| 645 | } else if (k
| |||
| 646 | ccv_dense_matrix_t* pha = phx; | |||
| 647 | phx = 0; | |||
| 648 | ccv_sample_down(pha, &phx, 0, 0, 0); | |||
| 649 | if (pha != a) | |||
| 650 | ccv_matrix_free(pha); | |||
| 651 | pyr = phx; | |||
| 652 | } | |||
| 653 | // Assumes if it is in the cache, then we can fetch these out of cache easily. | |||
| 654 | // (If the assumption is not true, worst case, we have the result cached for | |||
| 655 | // ccv_swt, but we don't for ccv_canny / ccv_close_outline / ccv_sobel, we will | |||
| 656 | // waste computation on these intermediate results. | |||
| 657 | ccv_dense_matrix_t* cc = 0; | |||
| 658 | ccv_canny(pyr, &cc, 0, params.size, params.low_thresh, params.high_thresh); | |||
| 659 | ccv_dense_matrix_t* c = 0; | |||
| 660 | ccv_close_outline(cc, &c, 0); | |||
| 661 | ccv_matrix_free(cc); | |||
| 662 | ccv_dense_matrix_t* dx = 0; | |||
| 663 | ccv_sobel(pyr, &dx, 0, params.size, 0); | |||
| 664 | ccv_dense_matrix_t* dy = 0; | |||
| 665 | ccv_sobel(pyr, &dy, 0, 0, params.size); | |||
| 666 | ccv_array_t* letters_a[2]; | |||
| 667 | ccv_array_t* textline_a[2]; | |||
| 668 | parallel_for(i, 2){ int i; for ((i) = 0; (i) < (2); (i)++) { { | |||
| 669 | ccv_dense_matrix_t* swt = 0; | |||
| 670 | params.direction = (i
| |||
| 671 | _ccv_swt(pyr, &swt, 0, params, c, dx, dy); | |||
| 672 | /* perform connected component analysis */ | |||
| 673 | letters_a[i] = _ccv_swt_connected_letters(pyr, swt, params); | |||
| 674 | ccv_matrix_free(swt); | |||
| 675 | textline_a[i] = _ccv_swt_merge_textline(letters_a[i], params); | |||
| 676 | } parallel_endfor} } | |||
| 677 | ccv_matrix_free(c); | |||
| 678 | ccv_matrix_free(dx); | |||
| 679 | ccv_matrix_free(dy); | |||
| 680 | if (pyr != phx) | |||
| 681 | ccv_matrix_free(pyr); | |||
| 682 | ccv_array_t* textline = textline_a[0]; | |||
| 683 | for (i = 0; i < textline_a[1]->rnum; i++) | |||
| 684 | ccv_array_push(textline, ccv_array_get(textline_a[1], i)((void*)(((char*)((textline_a[1])->data)) + (size_t)(textline_a [1])->rsize * (size_t)(i)))); | |||
| 685 | ccv_array_free(textline_a[1]); | |||
| 686 | ccv_array_t* idx = 0; | |||
| 687 | int ntl = ccv_array_group(textline, &idx, _ccv_is_same_textline, params.same_word_thresh); | |||
| 688 | ccv_array_t* words; | |||
| 689 | if (params.breakdown && ntl > 0) | |||
| 690 | { | |||
| 691 | ccv_array_t* const textline2 = ccv_array_new(sizeof(ccv_textline_t), ntl, 0); | |||
| 692 | ccv_array_zero(textline2); | |||
| 693 | textline2->rnum = ntl; | |||
| 694 | for (i = 0; i < textline->rnum; i++) | |||
| 695 | { | |||
| 696 | ccv_textline_t* r = (ccv_textline_t*)ccv_array_get(textline, i)((void*)(((char*)((textline)->data)) + (size_t)(textline)-> rsize * (size_t)(i))); | |||
| 697 | int k = *(int*)ccv_array_get(idx, i)((void*)(((char*)((idx)->data)) + (size_t)(idx)->rsize * (size_t)(i))); | |||
| 698 | ccv_textline_t* r2 = (ccv_textline_t*)ccv_array_get(textline2, k)((void*)(((char*)((textline2)->data)) + (size_t)(textline2 )->rsize * (size_t)(k))); | |||
| 699 | if (r2->rect.width < r->rect.width) | |||
| 700 | { | |||
| 701 | if (r2->letters) | |||
| 702 | ccfreefree(r2->letters); | |||
| 703 | *r2 = *r; | |||
| 704 | } else if (r->letters) { | |||
| 705 | ccfreefree(r->letters); | |||
| 706 | } | |||
| 707 | } | |||
| 708 | ccv_array_free(idx); | |||
| 709 | ccv_array_free(textline); | |||
| 710 | words = _ccv_swt_break_words(textline2, params); | |||
| 711 | for (i = 0; i < textline2->rnum; i++) | |||
| 712 | ccfreefree(((ccv_textline_t*)ccv_array_get(textline2, i)((void*)(((char*)((textline2)->data)) + (size_t)(textline2 )->rsize * (size_t)(i))))->letters); | |||
| 713 | ccv_array_free(textline2); | |||
| 714 | ccv_array_free(letters_a[0]); | |||
| 715 | ccv_array_free(letters_a[1]); | |||
| 716 | } else { | |||
| 717 | ccv_array_free(letters_a[0]); | |||
| 718 | ccv_array_free(letters_a[1]); | |||
| 719 | words = ccv_array_new(sizeof(ccv_rect_t), ntl, 0); | |||
| 720 | ccv_array_zero(words); | |||
| 721 | words->rnum = ntl; | |||
| 722 | for (i = 0; i < textline->rnum; i++) | |||
| 723 | { | |||
| 724 | ccv_textline_t* r = (ccv_textline_t*)ccv_array_get(textline, i)((void*)(((char*)((textline)->data)) + (size_t)(textline)-> rsize * (size_t)(i))); | |||
| 725 | if (r->letters) | |||
| 726 | ccfreefree(r->letters); | |||
| 727 | int k = *(int*)ccv_array_get(idx, i)((void*)(((char*)((idx)->data)) + (size_t)(idx)->rsize * (size_t)(i))); | |||
| 728 | ccv_rect_t* r2 = (ccv_rect_t*)ccv_array_get(words, k)((void*)(((char*)((words)->data)) + (size_t)(words)->rsize * (size_t)(k))); | |||
| 729 | if (r2->width * r2->height < r->rect.width * r->rect.height) | |||
| 730 | *r2 = r->rect; | |||
| 731 | } | |||
| 732 | ccv_array_free(idx); | |||
| 733 | ccv_array_free(textline); | |||
| 734 | } | |||
| 735 | if (params.scale_invariant) | |||
| 736 | { | |||
| 737 | for (i = 0; i < words->rnum; i++) | |||
| 738 | { | |||
| 739 | ccv_rect_t* rect = (ccv_rect_t*)ccv_array_get(words, i)((void*)(((char*)((words)->data)) + (size_t)(words)->rsize * (size_t)(i))); | |||
| 740 | rect->x = (int)(rect->x * cscale + 0.5); | |||
| 741 | rect->y = (int)(rect->y * cscale + 0.5); | |||
| 742 | rect->width = (int)(rect->width * cscale + 0.5); | |||
| 743 | rect->height = (int)(rect->height * cscale + 0.5); | |||
| 744 | ccv_array_push(all_words, rect); | |||
| 745 | } | |||
| 746 | ccv_array_free(words); | |||
| 747 | cscale *= scale; | |||
| 748 | } else | |||
| 749 | all_words = words; | |||
| 750 | } | |||
| 751 | if (params.scale_invariant && params.min_neighbors) | |||
| 752 | { | |||
| 753 | assert(all_words)((void) sizeof ((all_words) ? 1 : 0), __extension__ ({ if (all_words ) ; else __assert_fail ("all_words", "ccv_swt.c", 753, __extension__ __PRETTY_FUNCTION__); })); | |||
| 754 | // de-dup logic, similar to what BBF / DPM have | |||
| 755 | ccv_array_t* idx = 0; | |||
| 756 | int ntl = ccv_array_group(all_words, &idx, _ccv_is_same_textline, params.same_word_thresh); | |||
| 757 | ccv_array_t* new_words = ccv_array_new(sizeof(ccv_comp_t), ntl, 0); | |||
| 758 | ccv_array_zero(new_words); | |||
| 759 | new_words->rnum = ntl; | |||
| 760 | for (i = 0; i < all_words->rnum; i++) | |||
| 761 | { | |||
| 762 | ccv_rect_t* r1 = (ccv_rect_t*)ccv_array_get(all_words, i)((void*)(((char*)((all_words)->data)) + (size_t)(all_words )->rsize * (size_t)(i))); | |||
| 763 | int k = *(int*)ccv_array_get(idx, i)((void*)(((char*)((idx)->data)) + (size_t)(idx)->rsize * (size_t)(i))); | |||
| 764 | ccv_comp_t* r2 = (ccv_comp_t*)ccv_array_get(new_words, k)((void*)(((char*)((new_words)->data)) + (size_t)(new_words )->rsize * (size_t)(k))); | |||
| 765 | if (r2->neighbors) | |||
| 766 | { | |||
| 767 | ++r2->neighbors; | |||
| 768 | // simply pick the biggest | |||
| 769 | if (r1->width * r1->height > r2->rect.width * r2->rect.height) | |||
| 770 | r2->rect = *r1; | |||
| 771 | } else { | |||
| 772 | r2->rect = *r1; | |||
| 773 | r2->neighbors = 1; | |||
| 774 | } | |||
| 775 | } | |||
| 776 | ccv_array_free(idx); | |||
| 777 | ccv_array_free(all_words); | |||
| 778 | if (params.min_neighbors > 1) | |||
| 779 | { | |||
| 780 | // filter out min_neighbors | |||
| 781 | all_words = ccv_array_new(sizeof(ccv_comp_t), new_words->rnum / 2, 0); | |||
| 782 | for (i = 0; i < new_words->rnum; i++) | |||
| 783 | { | |||
| 784 | ccv_comp_t* comp = (ccv_comp_t*)ccv_array_get(new_words, i)((void*)(((char*)((new_words)->data)) + (size_t)(new_words )->rsize * (size_t)(i))); | |||
| 785 | int n = comp->neighbors; | |||
| 786 | if (n >= params.min_neighbors) | |||
| 787 | ccv_array_push(all_words, comp); | |||
| 788 | } | |||
| 789 | ccv_array_free(new_words); | |||
| 790 | } else | |||
| 791 | // just copy the pointer for min_neighbors == 1 | |||
| 792 | all_words = new_words; | |||
| 793 | } | |||
| 794 | if (phx != a) | |||
| 795 | ccv_matrix_free(phx); | |||
| 796 | return all_words; | |||
| 797 | } |