void ccv_close_outline(ccv_dense_matrix_t* a, ccv_dense_matrix_t** b, int type) { assert((CCV_GET_CHANNEL(a->type) == CCV_C1) && ((a->type & CCV_8U) || (a->type & CCV_32S) || (a->type & CCV_64S))); ccv_declare_derived_signature(sig, a->sig != 0, ccv_sign_with_literal("ccv_close_outline"), a->sig, CCV_EOF_SIGN); type = ((type == 0) || (type & CCV_32F) || (type & CCV_64F)) ? CCV_GET_DATA_TYPE(a->type) | CCV_C1 : CCV_GET_DATA_TYPE(type) | CCV_C1; ccv_dense_matrix_t* db = *b = ccv_dense_matrix_renew(*b, a->rows, a->cols, CCV_C1 | CCV_ALL_DATA_TYPE, type, sig); ccv_object_return_if_cached(, db); int i, j; unsigned char* a_ptr = a->data.u8; unsigned char* b_ptr = db->data.u8; ccv_zero(db); #define for_block(_for_get, _for_set_b, _for_get_b) \ for (i = 0; i < a->rows - 1; i++) \ { \ for (j = 0; j < a->cols - 1; j++) \ { \ if (!_for_get_b(b_ptr, j, 0)) \ _for_set_b(b_ptr, j, _for_get(a_ptr, j, 0), 0); \ if (_for_get(a_ptr, j, 0) && _for_get(a_ptr + a->step, j + 1, 0)) \ { \ _for_set_b(b_ptr + a->step, j, 1, 0); \ _for_set_b(b_ptr, j + 1, 1, 0); \ } \ if (_for_get(a_ptr + a->step, j, 0) && _for_get(a_ptr, j + 1, 0)) \ { \ _for_set_b(b_ptr, j, 1, 0); \ _for_set_b(b_ptr + a->step, j + 1, 1, 0); \ } \ } \ if (!_for_get_b(b_ptr, a->cols - 1, 0)) \ _for_set_b(b_ptr, a->cols - 1, _for_get(a_ptr, a->cols - 1, 0), 0); \ a_ptr += a->step; \ b_ptr += db->step; \ } \ for (j = 0; j < a->cols; j++) \ { \ if (!_for_get_b(b_ptr, j, 0)) \ _for_set_b(b_ptr, j, _for_get(a_ptr, j, 0), 0); \ } ccv_matrix_getter_integer_only(a->type, ccv_matrix_setter_getter_integer_only, db->type, for_block); #undef for_block }
static void _ccv_set_union_mser(ccv_dense_matrix_t* a, ccv_dense_matrix_t* h, ccv_dense_matrix_t* b, ccv_array_t* seq, ccv_mser_param_t params) { assert(params.direction == CCV_BRIGHT_TO_DARK || params.direction == CCV_DARK_TO_BRIGHT); int v, i, j; ccv_mser_node_t* node = (ccv_mser_node_t*)ccmalloc(sizeof(ccv_mser_node_t) * a->rows * a->cols); ccv_mser_node_t** rnode = (ccv_mser_node_t**)ccmalloc(sizeof(ccv_mser_node_t*) * a->rows * a->cols); if (params.range <= 0) params.range = 255; // put it in a block so that the memory allocated can be released in the end int* buck = (int*)alloca(sizeof(int) * (params.range + 2)); memset(buck, 0, sizeof(int) * (params.range + 2)); ccv_mser_node_t* pnode = node; // this for_block is the only computation that can be shared between dark to bright and bright to dark // two MSER alternatives, and it only occupies 10% of overall time, we won't share this computation // at all (also, we need to reinitialize node for the two passes anyway). if (h != 0) { unsigned char* aptr = a->data.u8; unsigned char* hptr = h->data.u8; #define for_block(_for_get_a, _for_get_h) \ for (i = 0; i < a->rows; i++) \ { \ for (j = 0; j < a->cols; j++) \ if (!_for_get_h(hptr, j, 0)) \ ++buck[_for_get_a(aptr, j, 0)]; \ aptr += a->step; \ hptr += h->step; \ } \ for (i = 1; i <= params.range; i++) \ buck[i] += buck[i - 1]; \ buck[params.range + 1] = buck[params.range]; \ aptr = a->data.u8; \ hptr = h->data.u8; \ for (i = 0; i < a->rows; i++) \ { \ for (j = 0; j < a->cols; j++) \ { \ _ccv_mser_init_node(pnode, j, i); \ if (!_for_get_h(hptr, j, 0)) \ rnode[--buck[_for_get_a(aptr, j, 0)]] = pnode; \ else \ pnode->shortcut = 0; /* this means the pnode is not available */ \ ++pnode; \ } \ aptr += a->step; \ hptr += h->step; \ } ccv_matrix_getter_integer_only_a(a->type, ccv_matrix_getter_integer_only, h->type, for_block); #undef for_block } else { unsigned char* aptr = a->data.u8; #define for_block(_, _for_get) \ for (i = 0; i < a->rows; i++) \ { \ for (j = 0; j < a->cols; j++) \ ++buck[_for_get(aptr, j, 0)]; \ aptr += a->step; \ } \ for (i = 1; i <= params.range; i++) \ buck[i] += buck[i - 1]; \ buck[params.range + 1] = buck[params.range]; \ aptr = a->data.u8; \ for (i = 0; i < a->rows; i++) \ { \ for (j = 0; j < a->cols; j++) \ { \ _ccv_mser_init_node(pnode, j, i); \ rnode[--buck[_for_get(aptr, j, 0)]] = pnode; \ ++pnode; \ } \ aptr += a->step; \ } ccv_matrix_getter_integer_only(a->type, for_block); #undef for_block } ccv_array_t* history_list = ccv_array_new(sizeof(ccv_mser_history_t), 64, 0); for (v = 0; v <= params.range; v++) { int range_segment = buck[params.direction == CCV_DARK_TO_BRIGHT ? v : params.range - v]; int range_segment_cap = buck[params.direction == CCV_DARK_TO_BRIGHT ? v + 1 : params.range - v + 1]; for (i = range_segment; i < range_segment_cap; i++) { pnode = rnode[i]; // try to merge pnode with its neighbors static int dx[] = {-1, 0, 1, -1, 1, -1, 0, 1}; static int dy[] = {-1, -1, -1, 0, 0, 1, 1, 1}; ccv_mser_node_t* node0 = _ccv_mser_find_root(pnode); for (j = 0; j < 8; j++) { int x = dx[j] + pnode->point.x; int y = dy[j] + pnode->point.y; if (x >= 0 && x < a->cols && y >= 0 && y < a->rows) { ccv_mser_node_t* nnode = pnode + dx[j] + dy[j] * a->cols; if (nnode->shortcut == 0) // this is a void node, skip continue; ccv_mser_node_t* node1 = _ccv_mser_find_root(nnode); if (node0 != node1) { // grep the extended root information ccv_mser_history_t* root0 = (node0->root >= 0) ? (ccv_mser_history_t*)ccv_array_get(history_list, node0->root) : 0; ccv_mser_history_t* root1 = (node1->root >= 0) ? (ccv_mser_history_t*)ccv_array_get(history_list, node1->root) : 0; // swap the node if root1 has higher rank, or larger in size, or root0 is non-existent if ((root0 && root1 && (root1->value > root0->value || (root1->value == root0->value && root1->rank > root0->rank) || (root1->value == root0->value && root1->rank == root0->rank && root1->size > root0->size))) || (root1 && !root0)) { ccv_mser_node_t* exnode = node0; node0 = node1; node1 = exnode; ccv_mser_history_t* root = root0; root0 = root1; root1 = root; } if (!root0) { ccv_mser_history_t root = { .rank = 0, .size = 1, .value = v, .shortcut = history_list->rnum, .parent = history_list->rnum, .head = node0, .tail = node1 }; node0->root = history_list->rnum; ccv_array_push(history_list, &root); root0 = (ccv_mser_history_t*)ccv_array_get(history_list, history_list->rnum - 1); assert(node1->root == -1); } else if (root0->value < v) { // conceal the old root as history (er), making a new one and pointing to it root0->shortcut = root0->parent = history_list->rnum; ccv_mser_history_t root = *root0; root.value = v; node0->root = history_list->rnum; ccv_array_push(history_list, &root); root0 = (ccv_mser_history_t*)ccv_array_get(history_list, history_list->rnum - 1); root1 = (node1->root >= 0) ? (ccv_mser_history_t*)ccv_array_get(history_list, node1->root) : 0; // the memory may be reallocated root0->rank = ccv_max(root0->rank, (root1 ? root1->rank : 0)) + 1; } if (root1) { if (root1->value < root0->value) // in this case, root1 is sealed as well root1->parent = node0->root; // thus, if root1->parent == itself && root1->shortcut != itself // it is voided, and not sealed root1->shortcut = node0->root; } // merge the two node1->shortcut = node0; root0->size += root1 ? root1->size : 1; /* insert one endless double link list to another, see illustration: * 0->1->2->3->4->5->0 * a->b->c->d->a * set 5.next (0.prev.next) point to a * set 0.prev point to d * set d.next (a.prev.next) point to 0 * set a.prev point to 5 * the result endless double link list will be: * 0->1->2->3->4->5->a->b->c->d->0 */ node0->prev->next = node1; ccv_mser_node_t* prev = node0->prev; node0->prev = node1->prev; node1->prev->next = node0; // consider self-referencing node1->prev = prev; root0->head = node0; root0->tail = node0->prev; } } }