1 /* -*-C-*-
2 ********************************************************************************
3 *
4 * File: seam.c (Formerly seam.c)
5 * Description:
6 * Author: Mark Seaman, OCR Technology
7 * Created: Fri Oct 16 14:37:00 1987
8 * Modified: Fri May 17 16:30:13 1991 (Mark Seaman) marks@hpgrlt
9 * Language: C
10 * Package: N/A
11 * Status: Reusable Software Component
12 *
13 * (c) Copyright 1987, Hewlett-Packard Company.
14 ** Licensed under the Apache License, Version 2.0 (the "License");
15 ** you may not use this file except in compliance with the License.
16 ** You may obtain a copy of the License at
17 ** http://www.apache.org/licenses/LICENSE-2.0
18 ** Unless required by applicable law or agreed to in writing, software
19 ** distributed under the License is distributed on an "AS IS" BASIS,
20 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21 ** See the License for the specific language governing permissions and
22 ** limitations under the License.
23 *
24 *********************************************************************************/
25 /*----------------------------------------------------------------------
26 I n c l u d e s
27 ----------------------------------------------------------------------*/
28 #include "seam.h"
29 #include "callcpp.h"
30 #include "structures.h"
31 #include "makechop.h"
32
33 #ifdef __UNIX__
34 #include <assert.h>
35 #endif
36
37 /*----------------------------------------------------------------------
38 V a r i a b l e s
39 ----------------------------------------------------------------------*/
40 #define NUM_STARTING_SEAMS 20
41
42 #define SEAMBLOCK 100 /* Cells per block */
43 makestructure (newseam, free_seam, printseam, SEAM,
44 freeseam, SEAMBLOCK, "SEAM", seamcount);
45
46 /*----------------------------------------------------------------------
47 Public Function Code
48 ----------------------------------------------------------------------*/
49 /**********************************************************************
50 * point_in_split
51 *
52 * Check to see if either of these points are present in the current
53 * split. Return TRUE if one of them is.
54 **********************************************************************/
point_in_split(SPLIT * split,EDGEPT * point1,EDGEPT * point2)55 bool point_in_split(SPLIT *split, EDGEPT *point1, EDGEPT *point2) {
56 return ((split) ?
57 ((exact_point (split->point1, point1) ||
58 exact_point (split->point1, point2) ||
59 exact_point (split->point2, point1) ||
60 exact_point (split->point2, point2)) ? TRUE : FALSE) : FALSE);
61 }
62
63
64 /**********************************************************************
65 * point_in_seam
66 *
67 * Check to see if either of these points are present in the current
68 * seam. Return TRUE if one of them is.
69 **********************************************************************/
point_in_seam(SEAM * seam,SPLIT * split)70 bool point_in_seam(SEAM *seam, SPLIT *split) {
71 return (point_in_split (seam->split1, split->point1, split->point2) ||
72 point_in_split (seam->split2, split->point1, split->point2) ||
73 point_in_split (seam->split3, split->point1, split->point2));
74 }
75
76
77 /**********************************************************************
78 * add_seam
79 *
80 * Add another seam to a collection of seams.
81 **********************************************************************/
add_seam(SEAMS seam_list,SEAM * seam)82 SEAMS add_seam(SEAMS seam_list, SEAM *seam) {
83 return (array_push (seam_list, seam));
84 }
85
86
87 /**********************************************************************
88 * combine_seam
89 *
90 * Combine two seam records into a single seam. Move the split
91 * references from the second seam to the first one. The argument
92 * convention is patterned after strcpy.
93 **********************************************************************/
combine_seams(SEAM * dest_seam,SEAM * source_seam)94 void combine_seams(SEAM *dest_seam, SEAM *source_seam) {
95 dest_seam->priority += source_seam->priority;
96 dest_seam->location += source_seam->location;
97 dest_seam->location /= 2;
98
99 if (source_seam->split1) {
100 if (!dest_seam->split1)
101 dest_seam->split1 = source_seam->split1;
102 else if (!dest_seam->split2)
103 dest_seam->split2 = source_seam->split1;
104 else if (!dest_seam->split3)
105 dest_seam->split3 = source_seam->split1;
106 else
107 cprintf ("combine_seam: Seam is too crowded, can't be combined !\n");
108 }
109 if (source_seam->split2) {
110 if (!dest_seam->split2)
111 dest_seam->split2 = source_seam->split2;
112 else if (!dest_seam->split3)
113 dest_seam->split3 = source_seam->split2;
114 else
115 cprintf ("combine_seam: Seam is too crowded, can't be combined !\n");
116 }
117 if (source_seam->split3) {
118 if (!dest_seam->split3)
119 dest_seam->split3 = source_seam->split3;
120 else
121 cprintf ("combine_seam: Seam is too crowded, can't be combined !\n");
122 }
123 free_seam(source_seam);
124 }
125
126
127 /**********************************************************************
128 * delete_seam
129 *
130 * Free this seam record and the splits that are attached to it.
131 **********************************************************************/
delete_seam(void * arg)132 void delete_seam(void *arg) { //SEAM *seam)
133 SEAM *seam = (SEAM *) arg;
134
135 if (seam) {
136 if (seam->split1)
137 delete_split (seam->split1);
138 if (seam->split2)
139 delete_split (seam->split2);
140 if (seam->split3)
141 delete_split (seam->split3);
142 free_seam(seam);
143 }
144 }
145
146
147 /**********************************************************************
148 * free_seam_list
149 *
150 * Free all the seams that have been allocated in this list. Reclaim
151 * the memory for each of the splits as well.
152 **********************************************************************/
free_seam_list(SEAMS seam_list)153 void free_seam_list(SEAMS seam_list) {
154 int x;
155
156 array_loop (seam_list, x) delete_seam (array_value (seam_list, x));
157 array_free(seam_list);
158 }
159
160
161 /**********************************************************************
162 * test_insert_seam
163 *
164 * Return true if insert_seam will succeed.
165 **********************************************************************/
test_insert_seam(SEAMS seam_list,int index,TBLOB * left_blob,TBLOB * first_blob)166 bool test_insert_seam(SEAMS seam_list,
167 int index,
168 TBLOB *left_blob,
169 TBLOB *first_blob) {
170 SEAM *test_seam;
171 TBLOB *blob;
172 int test_index;
173 int list_length;
174
175 list_length = array_count (seam_list);
176 for (test_index = 0, blob = first_blob->next;
177 test_index < index; test_index++, blob = blob->next) {
178 test_seam = (SEAM *) array_value (seam_list, test_index);
179 if (test_index + test_seam->widthp < index &&
180 test_seam->widthp + test_index == index - 1 &&
181 account_splits_right(test_seam, blob) < 0)
182 return false;
183 }
184 for (test_index = index, blob = left_blob->next;
185 test_index < list_length; test_index++, blob = blob->next) {
186 test_seam = (SEAM *) array_value (seam_list, test_index);
187 if (test_index - test_seam->widthn >= index &&
188 test_index - test_seam->widthn == index &&
189 account_splits_left(test_seam, first_blob, blob) < 0)
190 return false;
191 }
192 return true;
193 }
194
195 /**********************************************************************
196 * insert_seam
197 *
198 * Add another seam to a collection of seams at a particular location
199 * in the seam array.
200 **********************************************************************/
insert_seam(SEAMS seam_list,int index,SEAM * seam,TBLOB * left_blob,TBLOB * first_blob)201 SEAMS insert_seam(SEAMS seam_list,
202 int index,
203 SEAM *seam,
204 TBLOB *left_blob,
205 TBLOB *first_blob) {
206 SEAM *test_seam;
207 TBLOB *blob;
208 int test_index;
209 int list_length;
210
211 list_length = array_count (seam_list);
212 for (test_index = 0, blob = first_blob->next;
213 test_index < index; test_index++, blob = blob->next) {
214 test_seam = (SEAM *) array_value (seam_list, test_index);
215 if (test_index + test_seam->widthp >= index) {
216 test_seam->widthp++; /*got in the way */
217 }
218 else if (test_seam->widthp + test_index == index - 1) {
219 test_seam->widthp = account_splits_right(test_seam, blob);
220 if (test_seam->widthp < 0) {
221 cprintf ("Failed to find any right blob for a split!\n");
222 print_seam("New dud seam", seam);
223 print_seam("Failed seam", test_seam);
224 }
225 }
226 }
227 for (test_index = index, blob = left_blob->next;
228 test_index < list_length; test_index++, blob = blob->next) {
229 test_seam = (SEAM *) array_value (seam_list, test_index);
230 if (test_index - test_seam->widthn < index) {
231 test_seam->widthn++; /*got in the way */
232 }
233 else if (test_index - test_seam->widthn == index) {
234 test_seam->widthn = account_splits_left(test_seam, first_blob, blob);
235 if (test_seam->widthn < 0) {
236 cprintf ("Failed to find any left blob for a split!\n");
237 print_seam("New dud seam", seam);
238 print_seam("Failed seam", test_seam);
239 }
240 }
241 }
242 return (array_insert (seam_list, index, seam));
243 }
244
245
246 /**********************************************************************
247 * account_splits_right
248 *
249 * Account for all the splits by looking to the right.
250 * in the blob list.
251 **********************************************************************/
account_splits_right(SEAM * seam,TBLOB * blob)252 int account_splits_right(SEAM *seam, TBLOB *blob) {
253 inT8 found_em[3];
254 inT8 width;
255
256 found_em[0] = seam->split1 == NULL;
257 found_em[1] = seam->split2 == NULL;
258 found_em[2] = seam->split3 == NULL;
259 if (found_em[0] && found_em[1] && found_em[2])
260 return 0;
261 width = 0;
262 do {
263 if (!found_em[0])
264 found_em[0] = find_split_in_blob (seam->split1, blob);
265 if (!found_em[1])
266 found_em[1] = find_split_in_blob (seam->split2, blob);
267 if (!found_em[2])
268 found_em[2] = find_split_in_blob (seam->split3, blob);
269 if (found_em[0] && found_em[1] && found_em[2]) {
270 return width;
271 }
272 width++;
273 blob = blob->next;
274 }
275 while (blob != NULL);
276 return -1;
277 }
278
279
280 /**********************************************************************
281 * account_splits_left
282 *
283 * Account for all the splits by looking to the left.
284 * in the blob list.
285 **********************************************************************/
account_splits_left(SEAM * seam,TBLOB * blob,TBLOB * end_blob)286 int account_splits_left(SEAM *seam, TBLOB *blob, TBLOB *end_blob) {
287 static inT32 depth = 0;
288 static inT8 width;
289 static inT8 found_em[3];
290
291 if (blob != end_blob) {
292 depth++;
293 account_splits_left (seam, blob->next, end_blob);
294 depth--;
295 }
296 else {
297 found_em[0] = seam->split1 == NULL;
298 found_em[1] = seam->split2 == NULL;
299 found_em[2] = seam->split3 == NULL;
300 width = 0;
301 }
302 if (!found_em[0])
303 found_em[0] = find_split_in_blob (seam->split1, blob);
304 if (!found_em[1])
305 found_em[1] = find_split_in_blob (seam->split2, blob);
306 if (!found_em[2])
307 found_em[2] = find_split_in_blob (seam->split3, blob);
308 if (!found_em[0] || !found_em[1] || !found_em[2]) {
309 width++;
310 if (depth == 0) {
311 width = -1;
312 }
313 }
314 return width;
315 }
316
317
318 /**********************************************************************
319 * find_split_in_blob
320 *
321 * Return TRUE if the split is somewhere in this blob.
322 **********************************************************************/
find_split_in_blob(SPLIT * split,TBLOB * blob)323 bool find_split_in_blob(SPLIT *split, TBLOB *blob) {
324 TESSLINE *outline;
325
326 #if 0
327 for (outline = blob->outlines; outline != NULL; outline = outline->next)
328 if (is_split_outline (outline, split))
329 return TRUE;
330 return FALSE;
331 #endif
332 for (outline = blob->outlines; outline != NULL; outline = outline->next)
333 if (point_in_outline(split->point1, outline))
334 break;
335 if (outline == NULL)
336 return FALSE;
337 for (outline = blob->outlines; outline != NULL; outline = outline->next)
338 if (point_in_outline(split->point2, outline))
339 return TRUE;
340 return FALSE;
341 }
342
343
344 /**********************************************************************
345 * join_two_seams
346 *
347 * Merge these two seams into a new seam. Duplicate the split records
348 * in both of the input seams. Return the resultant seam.
349 **********************************************************************/
join_two_seams(SEAM * seam1,SEAM * seam2)350 SEAM *join_two_seams(SEAM *seam1, SEAM *seam2) {
351 SEAM *result = NULL;
352 SEAM *temp;
353
354 assert(seam1 &&seam2);
355
356 if (((seam1->split3 == NULL && seam2->split2 == NULL) ||
357 (seam1->split2 == NULL && seam2->split3 == NULL) ||
358 seam1->split1 == NULL ||
359 seam2->split1 == NULL) && (!shared_split_points (seam1, seam2))) {
360 clone_seam(result, seam1);
361 clone_seam(temp, seam2);
362 combine_seams(result, temp);
363 }
364 return (result);
365 }
366
367
368 /**********************************************************************
369 * new_seam
370 *
371 * Create a structure for a "seam" between two blobs. This data
372 * structure may actually hold up to three different splits.
373 * Initailization of this record is done by this routine.
374 **********************************************************************/
new_seam(PRIORITY priority,int x_location,SPLIT * split1,SPLIT * split2,SPLIT * split3)375 SEAM *new_seam(PRIORITY priority,
376 int x_location,
377 SPLIT *split1,
378 SPLIT *split2,
379 SPLIT *split3) {
380 SEAM *seam;
381
382 seam = newseam ();
383
384 seam->priority = priority;
385 seam->location = x_location;
386 seam->widthp = 0;
387 seam->widthn = 0;
388 seam->split1 = split1;
389 seam->split2 = split2;
390 seam->split3 = split3;
391
392 return (seam);
393 }
394
395
396 /**********************************************************************
397 * new_seam_list
398 *
399 * Create a collection of seam records in an array.
400 **********************************************************************/
new_seam_list()401 SEAMS new_seam_list() {
402 return (array_new (NUM_STARTING_SEAMS));
403 }
404
405
406 /**********************************************************************
407 * print_seam
408 *
409 * Print a list of splits. Show the coordinates of both points in
410 * each split.
411 **********************************************************************/
print_seam(const char * label,SEAM * seam)412 void print_seam(const char *label, SEAM *seam) {
413 if (seam) {
414 cprintf(label);
415 cprintf (" %6.2f @ %5d, p=%d, n=%d ",
416 seam->priority, seam->location, seam->widthp, seam->widthn);
417
418 print_split (seam->split1);
419
420 if (seam->split2) {
421 cprintf (", ");
422 print_split (seam->split2);
423
424 if (seam->split3) {
425 cprintf (", ");
426 print_split (seam->split3);
427 }
428 }
429 cprintf ("\n");
430 }
431 }
432
433
434 /**********************************************************************
435 * print_seams
436 *
437 * Print a list of splits. Show the coordinates of both points in
438 * each split.
439 **********************************************************************/
print_seams(const char * label,SEAMS seams)440 void print_seams(const char *label, SEAMS seams) {
441 int x;
442 char number[CHARS_PER_LINE];
443
444 if (seams) {
445 cprintf ("%s\n", label);
446 array_loop(seams, x) {
447 sprintf (number, "%2d: ", x);
448 print_seam (number, (SEAM *) array_value (seams, x));
449 }
450 cprintf ("\n");
451 }
452 }
453
454
455 /**********************************************************************
456 * shared_split_points
457 *
458 * Check these two seams to make sure that neither of them have two
459 * points in common. Return TRUE if any of the same points are present
460 * in any of the splits of both seams.
461 **********************************************************************/
shared_split_points(SEAM * seam1,SEAM * seam2)462 int shared_split_points(SEAM *seam1, SEAM *seam2) {
463 if (seam1 == NULL || seam2 == NULL)
464 return (FALSE);
465
466 if (seam2->split1 == NULL)
467 return (FALSE);
468 if (point_in_seam (seam1, seam2->split1))
469 return (TRUE);
470
471 if (seam2->split2 == NULL)
472 return (FALSE);
473 if (point_in_seam (seam1, seam2->split2))
474 return (TRUE);
475
476 if (seam2->split3 == NULL)
477 return (FALSE);
478 if (point_in_seam (seam1, seam2->split3))
479 return (TRUE);
480
481 return (FALSE);
482 }
483