Tesseract  3.02
tesseract-ocr/ccstruct/seam.cpp
Go to the documentation of this file.
00001 /* -*-C-*-
00002  ********************************************************************************
00003  *
00004  * File:        seam.c  (Formerly seam.c)
00005  * Description:
00006  * Author:       Mark Seaman, OCR Technology
00007  * Created:      Fri Oct 16 14:37:00 1987
00008  * Modified:     Fri May 17 16:30:13 1991 (Mark Seaman) marks@hpgrlt
00009  * Language:     C
00010  * Package:      N/A
00011  * Status:       Reusable Software Component
00012  *
00013  * (c) Copyright 1987, Hewlett-Packard Company.
00014  ** Licensed under the Apache License, Version 2.0 (the "License");
00015  ** you may not use this file except in compliance with the License.
00016  ** You may obtain a copy of the License at
00017  ** http://www.apache.org/licenses/LICENSE-2.0
00018  ** Unless required by applicable law or agreed to in writing, software
00019  ** distributed under the License is distributed on an "AS IS" BASIS,
00020  ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00021  ** See the License for the specific language governing permissions and
00022  ** limitations under the License.
00023  *
00024  *********************************************************************************/
00025 /*----------------------------------------------------------------------
00026               I n c l u d e s
00027 ----------------------------------------------------------------------*/
00028 #include "seam.h"
00029 #include "blobs.h"
00030 #include "callcpp.h"
00031 #include "structures.h"
00032 
00033 #ifdef __UNIX__
00034 #include <assert.h>
00035 #endif
00036 
00037 /*----------------------------------------------------------------------
00038               V a r i a b l e s
00039 ----------------------------------------------------------------------*/
00040 #define NUM_STARTING_SEAMS  20
00041 makestructure(newseam, free_seam, SEAM);
00042 
00043 /*----------------------------------------------------------------------
00044         Public Function Code
00045 ----------------------------------------------------------------------*/
00053 bool point_in_split(SPLIT *split, EDGEPT *point1, EDGEPT *point2) {
00054   return ((split) ? ((exact_point (split->point1, point1) ||
00055                       exact_point (split->point1, point2) ||
00056                       exact_point (split->point2, point1) ||
00057                       exact_point (split->point2, point2)) ? TRUE : FALSE)
00058                   : FALSE);
00059 }
00060 
00061 
00069 bool point_in_seam(SEAM *seam, SPLIT *split) {
00070   return (point_in_split(seam->split1, split->point1, split->point2) ||
00071           point_in_split(seam->split2, split->point1, split->point2) ||
00072           point_in_split(seam->split3, split->point1, split->point2));
00073 }
00074 
00081 bool point_used_by_split(SPLIT *split, EDGEPT *point) {
00082   if (split == NULL) return false;
00083   return point == split->point1 || point == split->point2;
00084 }
00085 
00092 bool point_used_by_seam(SEAM *seam, EDGEPT *point) {
00093   if (seam == NULL) return false;
00094   return point_used_by_split(seam->split1, point) ||
00095       point_used_by_split(seam->split2, point) ||
00096       point_used_by_split(seam->split3, point);
00097 }
00098 
00104 SEAMS add_seam(SEAMS seam_list, SEAM *seam) {
00105   return (array_push (seam_list, seam));
00106 }
00107 
00108 
00116 void combine_seams(SEAM *dest_seam, SEAM *source_seam) {
00117   dest_seam->priority += source_seam->priority;
00118   dest_seam->location += source_seam->location;
00119   dest_seam->location /= 2;
00120 
00121   if (source_seam->split1) {
00122     if (!dest_seam->split1)
00123       dest_seam->split1 = source_seam->split1;
00124     else if (!dest_seam->split2)
00125       dest_seam->split2 = source_seam->split1;
00126     else if (!dest_seam->split3)
00127       dest_seam->split3 = source_seam->split1;
00128     else
00129       cprintf("combine_seam: Seam is too crowded, can't be combined !\n");
00130   }
00131   if (source_seam->split2) {
00132     if (!dest_seam->split2)
00133       dest_seam->split2 = source_seam->split2;
00134     else if (!dest_seam->split3)
00135       dest_seam->split3 = source_seam->split2;
00136     else
00137       cprintf("combine_seam: Seam is too crowded, can't be combined !\n");
00138   }
00139   if (source_seam->split3) {
00140     if (!dest_seam->split3)
00141       dest_seam->split3 = source_seam->split3;
00142     else
00143       cprintf("combine_seam: Seam is too crowded, can't be combined !\n");
00144   }
00145   free_seam(source_seam);
00146 }
00147 
00148 
00154 void delete_seam(void *arg) {  //SEAM  *seam)
00155   SEAM *seam = (SEAM *) arg;
00156 
00157   if (seam) {
00158     if (seam->split1)
00159       delete_split(seam->split1);
00160     if (seam->split2)
00161       delete_split(seam->split2);
00162     if (seam->split3)
00163       delete_split(seam->split3);
00164     free_seam(seam);
00165   }
00166 }
00167 
00175 SEAMS start_seam_list(TBLOB *blobs) {
00176   TBLOB *blob;
00177   SEAMS seam_list;
00178   TPOINT location;
00179   /* Seam slot per char */
00180   seam_list = new_seam_list ();
00181 
00182   for (blob = blobs; blob->next != NULL; blob = blob->next) {
00183     TBOX bbox = blob->bounding_box();
00184     TBOX nbox = blob->next->bounding_box();
00185     location.x = (bbox.right() + nbox.left()) / 2;
00186     location.y = (bbox.bottom() + bbox.top() + nbox.bottom() + nbox.top()) / 4;
00187     seam_list = add_seam(seam_list,
00188         new_seam(0.0, location, NULL, NULL, NULL));
00189   }
00190 
00191   return seam_list;
00192 }
00193 
00200 void free_seam_list(SEAMS seam_list) {
00201   int x;
00202 
00203   array_loop(seam_list, x) delete_seam(array_value (seam_list, x));
00204   array_free(seam_list);
00205 }
00206 
00207 
00213 bool test_insert_seam(SEAMS seam_list,
00214                       int index,
00215                       TBLOB *left_blob,
00216                       TBLOB *first_blob) {
00217   SEAM *test_seam;
00218   TBLOB *blob;
00219   int test_index;
00220   int list_length;
00221 
00222   list_length = array_count (seam_list);
00223   for (test_index=0, blob=first_blob->next;
00224        test_index < index;
00225        test_index++, blob=blob->next) {
00226     test_seam = (SEAM *) array_value(seam_list, test_index);
00227     if (test_index + test_seam->widthp < index &&
00228         test_seam->widthp + test_index == index - 1 &&
00229         account_splits_right(test_seam, blob) < 0)
00230       return false;
00231   }
00232   for (test_index=index, blob=left_blob->next;
00233        test_index < list_length;
00234        test_index++, blob=blob->next) {
00235     test_seam = (SEAM *) array_value(seam_list, test_index);
00236     if (test_index - test_seam->widthn >= index &&
00237         test_index - test_seam->widthn == index &&
00238         account_splits_left(test_seam, first_blob, blob) < 0)
00239       return false;
00240   }
00241   return true;
00242 }
00243 
00250 SEAMS insert_seam(SEAMS seam_list,
00251                   int index,
00252                   SEAM *seam,
00253                   TBLOB *left_blob,
00254                   TBLOB *first_blob) {
00255   SEAM *test_seam;
00256   TBLOB *blob;
00257   int test_index;
00258   int list_length;
00259 
00260   list_length = array_count(seam_list);
00261   for (test_index=0, blob=first_blob->next;
00262        test_index < index;
00263        test_index++, blob=blob->next) {
00264     test_seam = (SEAM *) array_value(seam_list, test_index);
00265     if (test_index + test_seam->widthp >= index) {
00266       test_seam->widthp++;       /*got in the way */
00267     } else if (test_seam->widthp + test_index == index - 1) {
00268       test_seam->widthp = account_splits_right(test_seam, blob);
00269       if (test_seam->widthp < 0) {
00270         cprintf("Failed to find any right blob for a split!\n");
00271         print_seam("New dud seam", seam);
00272         print_seam("Failed seam", test_seam);
00273       }
00274     }
00275   }
00276   for (test_index=index, blob=left_blob->next;
00277        test_index < list_length;
00278        test_index++, blob=blob->next) {
00279     test_seam = (SEAM *) array_value(seam_list, test_index);
00280     if (test_index - test_seam->widthn < index) {
00281       test_seam->widthn++;       /*got in the way */
00282     } else if (test_index - test_seam->widthn == index) {
00283       test_seam->widthn = account_splits_left(test_seam, first_blob, blob);
00284       if (test_seam->widthn < 0) {
00285         cprintf("Failed to find any left blob for a split!\n");
00286         print_seam("New dud seam", seam);
00287         print_seam("Failed seam", test_seam);
00288       }
00289     }
00290   }
00291   return (array_insert (seam_list, index, seam));
00292 }
00293 
00294 
00301 int account_splits_right(SEAM *seam, TBLOB *blob) {
00302   inT8 found_em[3];
00303   inT8 width;
00304 
00305   found_em[0] = seam->split1 == NULL;
00306   found_em[1] = seam->split2 == NULL;
00307   found_em[2] = seam->split3 == NULL;
00308   if (found_em[0] && found_em[1] && found_em[2])
00309     return 0;
00310   width = 0;
00311   do {
00312     if (!found_em[0])
00313       found_em[0] = find_split_in_blob(seam->split1, blob);
00314     if (!found_em[1])
00315       found_em[1] = find_split_in_blob(seam->split2, blob);
00316     if (!found_em[2])
00317       found_em[2] = find_split_in_blob(seam->split3, blob);
00318     if (found_em[0] && found_em[1] && found_em[2]) {
00319       return width;
00320     }
00321     width++;
00322     blob = blob->next;
00323   } while (blob != NULL);
00324   return -1;
00325 }
00326 
00327 
00334 int account_splits_left(SEAM *seam, TBLOB *blob, TBLOB *end_blob) {
00335   inT32 depth = 0;
00336   inT8 width = 0;
00337   inT8 found_em[3];
00338   account_splits_left_helper(seam, blob, end_blob, &depth, &width, found_em);
00339   return width;
00340 }
00341 
00342 void account_splits_left_helper(SEAM *seam, TBLOB *blob, TBLOB *end_blob,
00343                                 inT32 *depth, inT8 *width, inT8* found_em) {
00344   if (blob != end_blob) {
00345     (*depth)++;
00346     account_splits_left_helper(seam, blob->next, end_blob,
00347                                depth, width, found_em);
00348     (*depth)--;
00349   } else {
00350     found_em[0] = seam->split1 == NULL;
00351     found_em[1] = seam->split2 == NULL;
00352     found_em[2] = seam->split3 == NULL;
00353     *width = 0;
00354   }
00355   if (!found_em[0])
00356     found_em[0] = find_split_in_blob(seam->split1, blob);
00357   if (!found_em[1])
00358     found_em[1] = find_split_in_blob(seam->split2, blob);
00359   if (!found_em[2])
00360     found_em[2] = find_split_in_blob(seam->split3, blob);
00361   if (!found_em[0] || !found_em[1] || !found_em[2]) {
00362     (*width)++;
00363     if (*depth == 0) {
00364       *width = -1;
00365     }
00366   }
00367 }
00368 
00369 
00375 bool find_split_in_blob(SPLIT *split, TBLOB *blob) {
00376   TESSLINE *outline;
00377 
00378   for (outline = blob->outlines; outline != NULL; outline = outline->next)
00379     if (outline->Contains(split->point1->pos))
00380       break;
00381   if (outline == NULL)
00382     return FALSE;
00383   for (outline = blob->outlines; outline != NULL; outline = outline->next)
00384     if (outline->Contains(split->point2->pos))
00385       return TRUE;
00386   return FALSE;
00387 }
00388 
00389 
00396 SEAM *join_two_seams(SEAM *seam1, SEAM *seam2) {
00397   SEAM *result = NULL;
00398   SEAM *temp;
00399 
00400   assert(seam1 &&seam2);
00401 
00402   if (((seam1->split3 == NULL && seam2->split2 == NULL) ||
00403        (seam1->split2 == NULL && seam2->split3 == NULL) ||
00404         seam1->split1 == NULL || seam2->split1 == NULL) &&
00405       (!shared_split_points(seam1, seam2))) {
00406     clone_seam(result, seam1);
00407     clone_seam(temp, seam2);
00408     combine_seams(result, temp);
00409   }
00410   return (result);
00411 }
00412 
00413 
00421 SEAM *new_seam(PRIORITY priority,
00422                const TPOINT& location,
00423                SPLIT *split1,
00424                SPLIT *split2,
00425                SPLIT *split3) {
00426   SEAM *seam;
00427 
00428   seam = newseam ();
00429 
00430   seam->priority = priority;
00431   seam->location = location;
00432   seam->widthp = 0;
00433   seam->widthn = 0;
00434   seam->split1 = split1;
00435   seam->split2 = split2;
00436   seam->split3 = split3;
00437 
00438   return (seam);
00439 }
00440 
00441 
00447 SEAMS new_seam_list() {
00448   return (array_new (NUM_STARTING_SEAMS));
00449 }
00450 
00451 
00458 void print_seam(const char *label, SEAM *seam) {
00459   if (seam) {
00460     cprintf(label);
00461     cprintf(" %6.2f @ (%d,%d), p=%d, n=%d ",
00462             seam->priority, seam->location.x, seam->location.y,
00463             seam->widthp, seam->widthn);
00464     print_split(seam->split1);
00465 
00466     if (seam->split2) {
00467       cprintf(",   ");
00468       print_split (seam->split2);
00469       if (seam->split3) {
00470         cprintf(",   ");
00471         print_split (seam->split3);
00472       }
00473     }
00474     cprintf ("\n");
00475   }
00476 }
00477 
00478 
00485 void print_seams(const char *label, SEAMS seams) {
00486   int x;
00487   char number[CHARS_PER_LINE];
00488 
00489   if (seams) {
00490     cprintf("%s\n", label);
00491     array_loop(seams, x) {
00492       sprintf(number, "%2d:   ", x);
00493       print_seam(number, (SEAM *) array_value(seams, x));
00494     }
00495     cprintf("\n");
00496   }
00497 }
00498 
00499 
00507 int shared_split_points(SEAM *seam1, SEAM *seam2) {
00508   if (seam1 == NULL || seam2 == NULL)
00509     return (FALSE);
00510 
00511   if (seam2->split1 == NULL)
00512     return (FALSE);
00513   if (point_in_seam(seam1, seam2->split1))
00514     return (TRUE);
00515 
00516   if (seam2->split2 == NULL)
00517     return (FALSE);
00518   if (point_in_seam(seam1, seam2->split2))
00519     return (TRUE);
00520 
00521   if (seam2->split3 == NULL)
00522     return (FALSE);
00523   if (point_in_seam(seam1, seam2->split3))
00524     return (TRUE);
00525 
00526   return (FALSE);
00527 }
00528 
00529 /**********************************************************************
00530  * break_pieces
00531  *
00532  * Break up the blobs in this chain so that they are all independent.
00533  * This operation should undo the affect of join_pieces.
00534  **********************************************************************/
00535 void break_pieces(TBLOB *blobs, SEAMS seams, inT16 start, inT16 end) {
00536   TESSLINE *outline = blobs->outlines;
00537   TBLOB *next_blob;
00538   inT16 x;
00539 
00540   for (x = start; x < end; x++)
00541     reveal_seam ((SEAM *) array_value (seams, x));
00542 
00543   next_blob = blobs->next;
00544 
00545   while (outline && next_blob) {
00546     if (outline->next == next_blob->outlines) {
00547       outline->next = NULL;
00548       outline = next_blob->outlines;
00549       next_blob = next_blob->next;
00550     }
00551     else {
00552       outline = outline->next;
00553     }
00554   }
00555 }
00556 
00557 
00558 /**********************************************************************
00559  * join_pieces
00560  *
00561  * Join a group of base level pieces into a single blob that can then
00562  * be classified.
00563  **********************************************************************/
00564 void join_pieces(TBLOB *piece_blobs, SEAMS seams, inT16 start, inT16 end) {
00565   TBLOB *next_blob;
00566   TBLOB *blob;
00567   inT16 x;
00568   TESSLINE *outline;
00569   SEAM *seam;
00570 
00571   for (x = 0, blob = piece_blobs; x < start; x++)
00572     blob = blob->next;
00573   next_blob = blob->next;
00574   outline = blob->outlines;
00575   if (!outline)
00576     return;
00577 
00578   while (x < end) {
00579     seam = (SEAM *) array_value (seams, x);
00580     if (x - seam->widthn >= start && x + seam->widthp < end)
00581       hide_seam(seam);
00582     while (outline->next)
00583       outline = outline->next;
00584     outline->next = next_blob->outlines;
00585     next_blob = next_blob->next;
00586 
00587     x++;
00588   }
00589 }
00590 
00591 
00592 /**********************************************************************
00593  * hide_seam
00594  *
00595  * Change the edge points that are referenced by this seam to make
00596  * them hidden edges.
00597  **********************************************************************/
00598 void hide_seam(SEAM *seam) {
00599   if (seam == NULL || seam->split1 == NULL)
00600     return;
00601   hide_edge_pair (seam->split1->point1, seam->split1->point2);
00602 
00603   if (seam->split2 == NULL)
00604     return;
00605   hide_edge_pair (seam->split2->point1, seam->split2->point2);
00606 
00607   if (seam->split3 == NULL)
00608     return;
00609   hide_edge_pair (seam->split3->point1, seam->split3->point2);
00610 }
00611 
00612 
00613 /**********************************************************************
00614  * hide_edge_pair
00615  *
00616  * Change the edge points that are referenced by this seam to make
00617  * them hidden edges.
00618  **********************************************************************/
00619 void hide_edge_pair(EDGEPT *pt1, EDGEPT *pt2) {
00620   EDGEPT *edgept;
00621 
00622   edgept = pt1;
00623   do {
00624     edgept->Hide();
00625     edgept = edgept->next;
00626   }
00627   while (!exact_point (edgept, pt2) && edgept != pt1);
00628   if (edgept == pt1) {
00629     /*              cprintf("Hid entire outline at (%d,%d)!!\n",
00630        edgept->pos.x,edgept->pos.y);                                */
00631   }
00632   edgept = pt2;
00633   do {
00634     edgept->Hide();
00635     edgept = edgept->next;
00636   }
00637   while (!exact_point (edgept, pt1) && edgept != pt2);
00638   if (edgept == pt2) {
00639     /*              cprintf("Hid entire outline at (%d,%d)!!\n",
00640        edgept->pos.x,edgept->pos.y);                                */
00641   }
00642 }
00643 
00644 
00645 /**********************************************************************
00646  * reveal_seam
00647  *
00648  * Change the edge points that are referenced by this seam to make
00649  * them hidden edges.
00650  **********************************************************************/
00651 void reveal_seam(SEAM *seam) {
00652   if (seam == NULL || seam->split1 == NULL)
00653     return;
00654   reveal_edge_pair (seam->split1->point1, seam->split1->point2);
00655 
00656   if (seam->split2 == NULL)
00657     return;
00658   reveal_edge_pair (seam->split2->point1, seam->split2->point2);
00659 
00660   if (seam->split3 == NULL)
00661     return;
00662   reveal_edge_pair (seam->split3->point1, seam->split3->point2);
00663 }
00664 
00665 
00666 /**********************************************************************
00667  * reveal_edge_pair
00668  *
00669  * Change the edge points that are referenced by this seam to make
00670  * them hidden edges.
00671  **********************************************************************/
00672 void reveal_edge_pair(EDGEPT *pt1, EDGEPT *pt2) {
00673   EDGEPT *edgept;
00674 
00675   edgept = pt1;
00676   do {
00677     edgept->Reveal();
00678     edgept = edgept->next;
00679   }
00680   while (!exact_point (edgept, pt2) && edgept != pt1);
00681   if (edgept == pt1) {
00682     /*              cprintf("Hid entire outline at (%d,%d)!!\n",
00683        edgept->pos.x,edgept->pos.y);                                */
00684   }
00685   edgept = pt2;
00686   do {
00687     edgept->Reveal();
00688     edgept = edgept->next;
00689   }
00690   while (!exact_point (edgept, pt1) && edgept != pt2);
00691   if (edgept == pt2) {
00692     /*              cprintf("Hid entire outline at (%d,%d)!!\n",
00693        edgept->pos.x,edgept->pos.y);                                */
00694   }
00695 }