/********************************************************************** * blobs_widths * * Compute the widths of a list of blobs. Return an array of the widths * and gaps. **********************************************************************/ WIDTH_RECORD *blobs_widths(TBLOB *blobs) { /*blob to compute on */ WIDTH_RECORD *width_record; TPOINT topleft; /*bounding box */ TPOINT botright; int i = 0; int blob_end; int num_blobs = count_blobs (blobs); /* Get memory */ width_record = (WIDTH_RECORD *) memalloc (sizeof (int) * num_blobs * 2); width_record->num_chars = num_blobs; blob_bounding_box(blobs, &topleft, &botright); width_record->widths[i++] = botright.x - topleft.x; /* First width */ blob_end = botright.x; for (TBLOB* blob = blobs->next; blob != NULL; blob = blob->next) { blob_bounding_box(blob, &topleft, &botright); width_record->widths[i++] = topleft.x - blob_end; width_record->widths[i++] = botright.x - topleft.x; blob_end = botright.x; } return (width_record); }
/** * @name start_seam_list * * Initialize a list of seams that match the original number of blobs * present in the starting segmentation. Each of the seams created * by this routine have location information only. */ SEAMS start_seam_list(TBLOB *blobs) { TBLOB *blob; SEAMS seam_list; TPOINT topleft; TPOINT botright; TPOINT location; /* Seam slot per char */ seam_list = new_seam_list (); for (blob = blobs; blob->next != NULL; blob = blob->next) { blob_bounding_box(blob, &topleft, &botright); location.x = botright.x; location.y = botright.y + topleft.y; blob_bounding_box (blob->next, &topleft, &botright); location.x += topleft.x; location.y += botright.y + topleft.y; location.x /= 2; location.y /= 4; seam_list = add_seam (seam_list, new_seam (0.0, location, NULL, NULL, NULL)); } return (seam_list); }
/********************************************************************** * total_containment * * Check to see if one of these outlines is totally contained within * the bounding box of the other. **********************************************************************/ inT16 total_containment(TBLOB *blob1, TBLOB *blob2) { TPOINT topleft1; TPOINT botright1; TPOINT topleft2; TPOINT botright2; blob_bounding_box(blob1, &topleft1, &botright1); blob_bounding_box(blob2, &topleft2, &botright2); return (bounds_inside (topleft1, botright1, topleft2, botright2) || bounds_inside (topleft2, botright2, topleft1, botright1)); }
TBOX TBLOB::bounding_box() const { TPOINT topleft; TPOINT botright; blob_bounding_box(this, &topleft, &botright); TBOX box(topleft.x, botright.y, botright.x, topleft.y); return box; }
/********************************************************************** * blobs_bounding_box * * Return the smallest extreme point that contain this word. **********************************************************************/ void blobs_bounding_box(TBLOB *blobs, TPOINT *topleft, TPOINT *botright) { TPOINT tl; TPOINT br; /* Start with first blob */ blob_bounding_box(blobs, topleft, botright); for (TBLOB* blob = blobs; blob != NULL; blob = blob->next) { blob_bounding_box(blob, &tl, &br); if (tl.x < topleft->x) topleft->x = tl.x; if (tl.y > topleft->y) topleft->y = tl.y; if (br.x > botright->x) botright->x = br.x; if (br.y < botright->y) botright->y = br.y; } }
/********************************************************************** * blob_origin * * Compute the origin of a compound blob, define to be the centre * of the bounding box. **********************************************************************/ void blob_origin(TBLOB *blob, /*blob to compute on */ TPOINT *origin) { /*return value */ TPOINT topleft; /*bounding box */ TPOINT botright; /*find bounding box */ blob_bounding_box(blob, &topleft, &botright); /*centre of box */ origin->x = (topleft.x + botright.x) / 2; origin->y = (topleft.y + botright.y) / 2; }
/********************************************************************** * record_blob_bounds * * Set up and initialize an array that holds the bounds of a set of * blobs. **********************************************************************/ BOUNDS_LIST record_blob_bounds(TBLOB *blobs) { TBLOB *blob; BOUNDS_LIST bounds; TPOINT topleft; TPOINT botright; INT16 x = 0; bounds = (BOUNDS_LIST) memalloc (count_blobs (blobs) * sizeof (BOUNDS)); iterate_blobs(blob, blobs) { blob_bounding_box(blob, &topleft, &botright); set_bounds_entry(bounds, x, topleft, botright); x++; }
/********************************************************************** * choose_best_seam * * Choose the best seam that can be created by assembling this a * collection of splits. A queue of all the possible seams is * maintained. Each new split received is placed in that queue with * its partial priority value. These values in the seam queue are * evaluated and combined until a good enough seam is found. If no * further good seams are being found then this function returns to the * caller, who will send more splits. If this function is called with * a split of NULL, then no further splits can be supplied by the * caller. **********************************************************************/ void choose_best_seam(SEAM_QUEUE seam_queue, SEAM_PILE *seam_pile, SPLIT *split, PRIORITY priority, SEAM **seam_result, TBLOB *blob) { SEAM *seam; TPOINT topleft; TPOINT botright; char str[80]; float my_priority; /* Add seam of split */ my_priority = priority; if (split != NULL) { seam = new_seam (my_priority, (split->point1->pos.x + split->point1->pos.x) / 2, split, NULL, NULL); if (chop_debug > 1) print_seam ("Partial priority ", seam); add_seam_to_queue (seam_queue, seam, (float) my_priority); if (my_priority > chop_good_split) return; } blob_bounding_box(blob, &topleft, &botright); /* Queue loop */ while (pop_next_seam (seam_queue, seam, my_priority)) { /* Set full priority */ my_priority = seam_priority (seam, topleft.x, botright.x); if (chop_debug) { sprintf (str, "Full my_priority %0.0f, ", my_priority); print_seam(str, seam); } if ((*seam_result == NULL || /* Replace answer */ (*seam_result)->priority > my_priority) && my_priority < chop_ok_split) { /* No crossing */ if (constrained_split (seam->split1, blob)) { delete_seam(*seam_result); clone_seam(*seam_result, seam); (*seam_result)->priority = my_priority; } else { delete_seam(seam); seam = NULL; my_priority = BAD_PRIORITY; } } if (my_priority < chop_good_split) { if (seam) delete_seam(seam); return; /* Made good answer */ } if (seam) { /* Combine with others */ if (array_count (*seam_pile) < MAX_NUM_SEAMS /*|| tessedit_truncate_chopper==0 */ ) { combine_seam(seam_queue, *seam_pile, seam); *seam_pile = array_push (*seam_pile, seam); } else delete_seam(seam); } my_priority = best_seam_priority (seam_queue); if ((my_priority > chop_ok_split) || (my_priority > chop_good_split && split)) return; } }