comparison mupdf-source/thirdparty/leptonica/src/sel1.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
1 /*====================================================================*
2 - Copyright (C) 2001 Leptonica. All rights reserved.
3 -
4 - Redistribution and use in source and binary forms, with or without
5 - modification, are permitted provided that the following conditions
6 - are met:
7 - 1. Redistributions of source code must retain the above copyright
8 - notice, this list of conditions and the following disclaimer.
9 - 2. Redistributions in binary form must reproduce the above
10 - copyright notice, this list of conditions and the following
11 - disclaimer in the documentation and/or other materials
12 - provided with the distribution.
13 -
14 - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15 - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16 - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17 - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY
18 - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23 - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *====================================================================*/
26
27
28 /*!
29 * \file sel1.c
30 * <pre>
31 *
32 * Basic ops on Sels and Selas
33 *
34 * Create/destroy/copy:
35 * SELA *selaCreate()
36 * void selaDestroy()
37 * SEL *selCreate()
38 * void selDestroy()
39 * SEL *selCopy()
40 * SEL *selCreateBrick()
41 * SEL *selCreateComb()
42 *
43 * Helper proc:
44 * l_int32 **create2dIntArray()
45 *
46 * Extension of sela:
47 * SELA *selaAddSel()
48 * static l_int32 selaExtendArray()
49 *
50 * Accessors:
51 * l_int32 selaGetCount()
52 * SEL *selaGetSel()
53 * char *selGetName()
54 * l_int32 selSetName()
55 * l_int32 selaFindSelByName()
56 * l_int32 selGetElement()
57 * l_int32 selSetElement()
58 * l_int32 selGetParameters()
59 * l_int32 selSetOrigin()
60 * l_int32 selGetTypeAtOrigin()
61 * char *selaGetBrickName()
62 * char *selaGetCombName()
63 * static char *selaComputeCompositeParameters()
64 * l_int32 getCompositeParameters()
65 * SARRAY *selaGetSelnames()
66 *
67 * Max translations for erosion and hmt
68 * l_int32 selFindMaxTranslations()
69 *
70 * Rotation by multiples of 90 degrees
71 * SEL *selRotateOrth()
72 *
73 * Sela and Sel serialized I/O
74 * SELA *selaRead()
75 * SELA *selaReadStream()
76 * SEL *selRead()
77 * SEL *selReadStream()
78 * l_int32 selaWrite()
79 * l_int32 selaWriteStream()
80 * l_int32 selWrite()
81 * l_int32 selWriteStream()
82 *
83 * Building custom hit-miss sels from compiled strings
84 * SEL *selCreateFromString()
85 * char *selPrintToString() [for debugging]
86 *
87 * Building custom hit-miss sels from a simple file format
88 * SELA *selaCreateFromFile()
89 * static SEL *selCreateFromSArray()
90 *
91 * Making hit-only sels from Pta and Pix
92 * SEL *selCreateFromPta()
93 * SEL *selCreateFromPix()
94 *
95 * Making hit-miss sels from Pix and image files
96 * SEL *selReadFromColorImage()
97 * SEL *selCreateFromColorPix()
98 SELA *selaCreateFromColorPixa()
99 *
100 * Printable display of sel
101 * PIX *selDisplayInPix()
102 * PIX *selaDisplayInPix()
103 *
104 * Usage notes:
105 * In this file we have seven functions that make sels:
106 * (1) selCreate(), with input (h, w, [name])
107 * The generic function. Roll your own, using selSetElement().
108 * (2) selCreateBrick(), with input (h, w, cy, cx, val)
109 * The most popular function. Makes a rectangular sel of
110 * all hits, misses or don't-cares. We have many morphology
111 * operations that create a sel of all hits, use it, and
112 * destroy it.
113 * (3) selCreateFromString() with input (text, h, w, [name])
114 * Adam Langley's clever function, allows you to make a hit-miss
115 * sel from a string in code that is geometrically laid out
116 * just like the actual sel.
117 * (4) selaCreateFromFile() with input (filename)
118 * This parses a simple file format to create an array of
119 * hit-miss sels. The sel data uses the same encoding
120 * as in (3), with geometrical layout enforced.
121 * (5) selCreateFromPta() with input (pta, cy, cx, [name])
122 * Another way to make a sel with only hits.
123 * (6) selCreateFromPix() with input (pix, cy, cx, [name])
124 * Yet another way to make a sel from hits.
125 * (7) selCreateFromColorPix() with input (pix, name).
126 * Another way to make a general hit-miss sel, starting with
127 * an image editor.
128 * In addition, there are three functions in selgen.c that
129 * automatically generate a hit-miss sel from a pix and
130 * a number of parameters. This is useful for problems like
131 * "find all patterns that look like this one."
132 *
133 * Consistency, being the hobgoblin of small minds,
134 * is adhered to here in the dimensioning and accessing of sels.
135 * Everything is done in standard matrix (row, column) order.
136 * When we set specific elements in a sel, we likewise use
137 * (row, col) ordering:
138 * selSetElement(), with input (row, col, type)
139 * </pre>
140 */
141
142 #ifdef HAVE_CONFIG_H
143 #include <config_auto.h>
144 #endif /* HAVE_CONFIG_H */
145
146 #include <string.h>
147 #include "allheaders.h"
148
149 /* Bounds on sel ptr array size */
150 static const l_uint32 MaxPtrArraySize = 10000;
151 static const l_int32 InitialPtrArraySize = 50; /*!< n'importe quoi */
152
153 /* Bounds on kernel size */
154 static const l_uint32 MaxKernelSize = 10000;
155
156 /* Bounds on pix template size */
157 static const l_uint32 MaxPixTemplateSize = 300;
158 static const l_uint32 MaxPixTemplateHits = 3000;
159
160 /* Static functions */
161 static l_int32 selaExtendArray(SELA *sela);
162 static SEL *selCreateFromSArray(SARRAY *sa, l_int32 first, l_int32 last);
163
164 struct CompParameterMap
165 {
166 l_int32 size;
167 l_int32 size1;
168 l_int32 size2;
169 char selnameh1[20];
170 char selnameh2[20];
171 char selnamev1[20];
172 char selnamev2[20];
173 };
174
175 static const struct CompParameterMap comp_parameter_map[] =
176 { { 2, 2, 1, "sel_2h", "", "sel_2v", "" },
177 { 3, 3, 1, "sel_3h", "", "sel_3v", "" },
178 { 4, 2, 2, "sel_2h", "sel_comb_4h", "sel_2v", "sel_comb_4v" },
179 { 5, 5, 1, "sel_5h", "", "sel_5v", "" },
180 { 6, 3, 2, "sel_3h", "sel_comb_6h", "sel_3v", "sel_comb_6v" },
181 { 7, 7, 1, "sel_7h", "", "sel_7v", "" },
182 { 8, 4, 2, "sel_4h", "sel_comb_8h", "sel_4v", "sel_comb_8v" },
183 { 9, 3, 3, "sel_3h", "sel_comb_9h", "sel_3v", "sel_comb_9v" },
184 { 10, 5, 2, "sel_5h", "sel_comb_10h", "sel_5v", "sel_comb_10v" },
185 { 11, 4, 3, "sel_4h", "sel_comb_12h", "sel_4v", "sel_comb_12v" },
186 { 12, 4, 3, "sel_4h", "sel_comb_12h", "sel_4v", "sel_comb_12v" },
187 { 13, 4, 3, "sel_4h", "sel_comb_12h", "sel_4v", "sel_comb_12v" },
188 { 14, 7, 2, "sel_7h", "sel_comb_14h", "sel_7v", "sel_comb_14v" },
189 { 15, 5, 3, "sel_5h", "sel_comb_15h", "sel_5v", "sel_comb_15v" },
190 { 16, 4, 4, "sel_4h", "sel_comb_16h", "sel_4v", "sel_comb_16v" },
191 { 17, 4, 4, "sel_4h", "sel_comb_16h", "sel_4v", "sel_comb_16v" },
192 { 18, 6, 3, "sel_6h", "sel_comb_18h", "sel_6v", "sel_comb_18v" },
193 { 19, 5, 4, "sel_5h", "sel_comb_20h", "sel_5v", "sel_comb_20v" },
194 { 20, 5, 4, "sel_5h", "sel_comb_20h", "sel_5v", "sel_comb_20v" },
195 { 21, 7, 3, "sel_7h", "sel_comb_21h", "sel_7v", "sel_comb_21v" },
196 { 22, 11, 2, "sel_11h", "sel_comb_22h", "sel_11v", "sel_comb_22v" },
197 { 23, 6, 4, "sel_6h", "sel_comb_24h", "sel_6v", "sel_comb_24v" },
198 { 24, 6, 4, "sel_6h", "sel_comb_24h", "sel_6v", "sel_comb_24v" },
199 { 25, 5, 5, "sel_5h", "sel_comb_25h", "sel_5v", "sel_comb_25v" },
200 { 26, 5, 5, "sel_5h", "sel_comb_25h", "sel_5v", "sel_comb_25v" },
201 { 27, 9, 3, "sel_9h", "sel_comb_27h", "sel_9v", "sel_comb_27v" },
202 { 28, 7, 4, "sel_7h", "sel_comb_28h", "sel_7v", "sel_comb_28v" },
203 { 29, 6, 5, "sel_6h", "sel_comb_30h", "sel_6v", "sel_comb_30v" },
204 { 30, 6, 5, "sel_6h", "sel_comb_30h", "sel_6v", "sel_comb_30v" },
205 { 31, 6, 5, "sel_6h", "sel_comb_30h", "sel_6v", "sel_comb_30v" },
206 { 32, 8, 4, "sel_8h", "sel_comb_32h", "sel_8v", "sel_comb_32v" },
207 { 33, 11, 3, "sel_11h", "sel_comb_33h", "sel_11v", "sel_comb_33v" },
208 { 34, 7, 5, "sel_7h", "sel_comb_35h", "sel_7v", "sel_comb_35v" },
209 { 35, 7, 5, "sel_7h", "sel_comb_35h", "sel_7v", "sel_comb_35v" },
210 { 36, 6, 6, "sel_6h", "sel_comb_36h", "sel_6v", "sel_comb_36v" },
211 { 37, 6, 6, "sel_6h", "sel_comb_36h", "sel_6v", "sel_comb_36v" },
212 { 38, 6, 6, "sel_6h", "sel_comb_36h", "sel_6v", "sel_comb_36v" },
213 { 39, 13, 3, "sel_13h", "sel_comb_39h", "sel_13v", "sel_comb_39v" },
214 { 40, 8, 5, "sel_8h", "sel_comb_40h", "sel_8v", "sel_comb_40v" },
215 { 41, 7, 6, "sel_7h", "sel_comb_42h", "sel_7v", "sel_comb_42v" },
216 { 42, 7, 6, "sel_7h", "sel_comb_42h", "sel_7v", "sel_comb_42v" },
217 { 43, 7, 6, "sel_7h", "sel_comb_42h", "sel_7v", "sel_comb_42v" },
218 { 44, 11, 4, "sel_11h", "sel_comb_44h", "sel_11v", "sel_comb_44v" },
219 { 45, 9, 5, "sel_9h", "sel_comb_45h", "sel_9v", "sel_comb_45v" },
220 { 46, 9, 5, "sel_9h", "sel_comb_45h", "sel_9v", "sel_comb_45v" },
221 { 47, 8, 6, "sel_8h", "sel_comb_48h", "sel_8v", "sel_comb_48v" },
222 { 48, 8, 6, "sel_8h", "sel_comb_48h", "sel_8v", "sel_comb_48v" },
223 { 49, 7, 7, "sel_7h", "sel_comb_49h", "sel_7v", "sel_comb_49v" },
224 { 50, 10, 5, "sel_10h", "sel_comb_50h", "sel_10v", "sel_comb_50v" },
225 { 51, 10, 5, "sel_10h", "sel_comb_50h", "sel_10v", "sel_comb_50v" },
226 { 52, 13, 4, "sel_13h", "sel_comb_52h", "sel_13v", "sel_comb_52v" },
227 { 53, 9, 6, "sel_9h", "sel_comb_54h", "sel_9v", "sel_comb_54v" },
228 { 54, 9, 6, "sel_9h", "sel_comb_54h", "sel_9v", "sel_comb_54v" },
229 { 55, 11, 5, "sel_11h", "sel_comb_55h", "sel_11v", "sel_comb_55v" },
230 { 56, 8, 7, "sel_8h", "sel_comb_56h", "sel_8v", "sel_comb_56v" },
231 { 57, 8, 7, "sel_8h", "sel_comb_56h", "sel_8v", "sel_comb_56v" },
232 { 58, 8, 7, "sel_8h", "sel_comb_56h", "sel_8v", "sel_comb_56v" },
233 { 59, 10, 6, "sel_10h", "sel_comb_60h", "sel_10v", "sel_comb_60v" },
234 { 60, 10, 6, "sel_10h", "sel_comb_60h", "sel_10v", "sel_comb_60v" },
235 { 61, 10, 6, "sel_10h", "sel_comb_60h", "sel_10v", "sel_comb_60v" },
236 { 62, 9, 7, "sel_9h", "sel_comb_63h", "sel_9v", "sel_comb_63v" },
237 { 63, 9, 7, "sel_9h", "sel_comb_63h", "sel_9v", "sel_comb_63v" } };
238
239
240
241 /*------------------------------------------------------------------------*
242 * Create / Destroy / Copy *
243 *------------------------------------------------------------------------*/
244 /*!
245 * \brief selaCreate()
246 *
247 * \param[in] n initial number of sel ptrs; use 0 for default
248 * \return sela, or NULL on error
249 */
250 SELA *
251 selaCreate(l_int32 n)
252 {
253 SELA *sela;
254
255 if (n <= 0 || n > (l_int32)MaxPtrArraySize)
256 n = InitialPtrArraySize;
257
258 /* Make array of sel ptrs */
259 sela = (SELA *)LEPT_CALLOC(1, sizeof(SELA));
260 sela->nalloc = n;
261 sela->n = 0;
262 sela->sel = (SEL **)LEPT_CALLOC(n, sizeof(SEL *));
263 return sela;
264 }
265
266
267 /*!
268 * \brief selaDestroy()
269 *
270 * \param[in,out] psela will be set to null before returning
271 * \return void
272 */
273 void
274 selaDestroy(SELA **psela)
275 {
276 SELA *sela;
277 l_int32 i;
278
279 if (!psela) return;
280 if ((sela = *psela) == NULL)
281 return;
282
283 for (i = 0; i < sela->n; i++)
284 selDestroy(&sela->sel[i]);
285 LEPT_FREE(sela->sel);
286 LEPT_FREE(sela);
287 *psela = NULL;
288 }
289
290
291 /*!
292 * \brief selCreate()
293 *
294 * \param[in] height
295 * \param[in] width
296 * \param[in] name [optional] sel name; can be null
297 * \return sel, or NULL on error
298 *
299 * <pre>
300 * Notes:
301 * (1) selCreate() initializes all values to 0.
302 * (2) After this call, (cy,cx) and nonzero data values must be
303 * assigned. If a text name is not assigned here, it will
304 * be needed later when the sel is put into a sela.
305 * </pre>
306 */
307 SEL *
308 selCreate(l_int32 height,
309 l_int32 width,
310 const char *name)
311 {
312 SEL *sel;
313
314 sel = (SEL *)LEPT_CALLOC(1, sizeof(SEL));
315 if (name)
316 sel->name = stringNew(name);
317 sel->sy = height;
318 sel->sx = width;
319 if ((sel->data = create2dIntArray(height, width)) == NULL) {
320 LEPT_FREE(sel->name);
321 LEPT_FREE(sel);
322 return (SEL *)ERROR_PTR("data not allocated", __func__, NULL);
323 }
324
325 return sel;
326 }
327
328
329 /*!
330 * \brief selDestroy()
331 *
332 * \param[in,out] psel will be set to null before returning
333 * \return void
334 */
335 void
336 selDestroy(SEL **psel)
337 {
338 l_int32 i;
339 SEL *sel;
340
341 if (psel == NULL) {
342 L_WARNING("ptr address is NULL!\n", __func__);
343 return;
344 }
345 if ((sel = *psel) == NULL)
346 return;
347
348 for (i = 0; i < sel->sy; i++)
349 LEPT_FREE(sel->data[i]);
350 LEPT_FREE(sel->data);
351 if (sel->name)
352 LEPT_FREE(sel->name);
353 LEPT_FREE(sel);
354 *psel = NULL;
355 }
356
357
358 /*!
359 * \brief selCopy()
360 *
361 * \param[in] sel
362 * \return a copy of the sel, or NULL on error
363 */
364 SEL *
365 selCopy(SEL *sel)
366 {
367 l_int32 sx, sy, cx, cy, i, j;
368 SEL *csel;
369
370 if (!sel)
371 return (SEL *)ERROR_PTR("sel not defined", __func__, NULL);
372
373 csel = (SEL *)LEPT_CALLOC(1, sizeof(SEL));
374 selGetParameters(sel, &sy, &sx, &cy, &cx);
375 csel->sy = sy;
376 csel->sx = sx;
377 csel->cy = cy;
378 csel->cx = cx;
379
380 if ((csel->data = create2dIntArray(sy, sx)) == NULL) {
381 LEPT_FREE(csel);
382 return (SEL *)ERROR_PTR("sel data not made", __func__, NULL);
383 }
384
385 for (i = 0; i < sy; i++)
386 for (j = 0; j < sx; j++)
387 csel->data[i][j] = sel->data[i][j];
388
389 if (sel->name)
390 csel->name = stringNew(sel->name);
391
392 return csel;
393 }
394
395
396 /*!
397 * \brief selCreateBrick()
398 *
399 * \param[in] h, w height, width
400 * \param[in] cy, cx origin, relative to UL corner at 0,0
401 * \param[in] type SEL_HIT, SEL_MISS, or SEL_DONT_CARE
402 * \return sel, or NULL on error
403 *
404 * <pre>
405 * Notes:
406 * (1) This is a rectangular sel of all hits, misses or don't cares.
407 * </pre>
408 */
409 SEL *
410 selCreateBrick(l_int32 h,
411 l_int32 w,
412 l_int32 cy,
413 l_int32 cx,
414 l_int32 type)
415 {
416 l_int32 i, j;
417 SEL *sel;
418
419 if (h <= 0 || w <= 0)
420 return (SEL *)ERROR_PTR("h and w must both be > 0", __func__, NULL);
421 if (type != SEL_HIT && type != SEL_MISS && type != SEL_DONT_CARE)
422 return (SEL *)ERROR_PTR("invalid sel element type", __func__, NULL);
423
424 if ((sel = selCreate(h, w, NULL)) == NULL)
425 return (SEL *)ERROR_PTR("sel not made", __func__, NULL);
426 selSetOrigin(sel, cy, cx);
427 for (i = 0; i < h; i++)
428 for (j = 0; j < w; j++)
429 sel->data[i][j] = type;
430
431 return sel;
432 }
433
434
435 /*!
436 * \brief selCreateComb()
437 *
438 * \param[in] factor1 contiguous space between comb tines
439 * \param[in] factor2 number of comb tines
440 * \param[in] direction L_HORIZ, L_VERT
441 * \return sel, or NULL on error
442 *
443 * <pre>
444 * Notes:
445 * (1) This generates a comb Sel of hits with the origin as
446 * near the center as possible.
447 * (2) In use, this is complemented by a brick sel of size %factor1,
448 * Both brick and comb sels are made by selectComposableSels().
449 * </pre>
450 */
451 SEL *
452 selCreateComb(l_int32 factor1,
453 l_int32 factor2,
454 l_int32 direction)
455 {
456 l_int32 i, size, z;
457 SEL *sel;
458
459 if (factor1 < 1 || factor2 < 1)
460 return (SEL *)ERROR_PTR("factors must be >= 1", __func__, NULL);
461 if (direction != L_HORIZ && direction != L_VERT)
462 return (SEL *)ERROR_PTR("invalid direction", __func__, NULL);
463
464 size = factor1 * factor2;
465 if (direction == L_HORIZ) {
466 if ((sel = selCreate(1, size, NULL)) == NULL)
467 return (SEL *)ERROR_PTR("horiz sel not made", __func__, NULL);
468 selSetOrigin(sel, 0, size / 2);
469 } else {
470 if ((sel = selCreate(size, 1, NULL)) == NULL)
471 return (SEL *)ERROR_PTR("vert sel not made", __func__, NULL);
472 selSetOrigin(sel, size / 2, 0);
473 }
474
475 /* Lay down the elements of the comb */
476 for (i = 0; i < factor2; i++) {
477 z = factor1 / 2 + i * factor1;
478 /* lept_stderr("i = %d, factor1 = %d, factor2 = %d, z = %d\n",
479 i, factor1, factor2, z); */
480 if (direction == L_HORIZ)
481 selSetElement(sel, 0, z, SEL_HIT);
482 else
483 selSetElement(sel, z, 0, SEL_HIT);
484 }
485
486 return sel;
487 }
488
489
490 /*!
491 * \brief create2dIntArray()
492 *
493 * \param[in] sy rows == height
494 * \param[in] sx columns == width
495 * \return doubly indexed array i.e., an array of sy row pointers,
496 * each of which points to an array of sx ints
497 *
498 * <pre>
499 * Notes:
500 * (1) The array[sy][sx] is indexed in standard "matrix notation",
501 * with the row index first.
502 * </pre>
503 */
504 l_int32 **
505 create2dIntArray(l_int32 sy,
506 l_int32 sx)
507 {
508 l_int32 i;
509 l_int32 **array;
510
511 if (sx <= 0 || sx > (l_int32)MaxKernelSize)
512 return (l_int32 **)ERROR_PTR("sx out of bounds", __func__, NULL);
513 if (sy <= 0 || sy > (l_int32)MaxKernelSize)
514 return (l_int32 **)ERROR_PTR("sy out of bounds", __func__, NULL);
515
516 array = (l_int32 **)LEPT_CALLOC(sy, sizeof(l_int32 *));
517 for (i = 0; i < sy; i++)
518 array[i] = (l_int32 *)LEPT_CALLOC(sx, sizeof(l_int32));
519 return array;
520 }
521
522
523 /*------------------------------------------------------------------------*
524 * Extension of sela *
525 *------------------------------------------------------------------------*/
526 /*!
527 * \brief selaAddSel()
528 *
529 * \param[in] sela
530 * \param[in] sel to be added
531 * \param[in] selname ignored if already defined in sel;
532 * req'd in sel when added to a sela
533 * \param[in] copyflag L_INSERT or L_COPY
534 * \return 0 if OK; 1 on error
535 *
536 * <pre>
537 * Notes:
538 * (1) This adds a sel, either inserting or making a copy.
539 * (2) Because every sel in a sela must have a name, it copies
540 * the input name if necessary. You can input NULL for
541 * selname if the sel already has a name.
542 * </pre>
543 */
544 l_ok
545 selaAddSel(SELA *sela,
546 SEL *sel,
547 const char *selname,
548 l_int32 copyflag)
549 {
550 l_int32 n;
551 SEL *csel;
552
553 if (!sela)
554 return ERROR_INT("sela not defined", __func__, 1);
555 if (!sel)
556 return ERROR_INT("sel not defined", __func__, 1);
557 if (!sel->name && !selname)
558 return ERROR_INT("added sel must have name", __func__, 1);
559 if (copyflag != L_INSERT && copyflag != L_COPY)
560 return ERROR_INT("invalid copyflag", __func__, 1);
561
562 if (copyflag == L_COPY) {
563 if ((csel = selCopy(sel)) == NULL)
564 return ERROR_INT("csel not made", __func__, 1);
565 } else { /* copyflag == L_INSERT */
566 csel = sel;
567 }
568 if (!csel->name)
569 csel->name = stringNew(selname);
570
571 n = selaGetCount(sela);
572 if (n >= sela->nalloc) {
573 if (selaExtendArray(sela)) {
574 if (copyflag != L_INSERT)
575 selDestroy(&csel);
576 return ERROR_INT("extension failed", __func__, 1);
577 }
578 }
579
580 sela->sel[n] = csel;
581 sela->n++;
582 return 0;
583 }
584
585
586 /*!
587 * \brief selaExtendArray()
588 *
589 * \param[in] sela
590 * \return 0 if OK; 1 on error
591 */
592 static l_int32
593 selaExtendArray(SELA *sela)
594 {
595 if (!sela)
596 return ERROR_INT("sela not defined", __func__, 1);
597
598 if ((sela->sel = (SEL **)reallocNew((void **)&sela->sel,
599 sizeof(SEL *) * sela->nalloc,
600 2 * sizeof(SEL *) * sela->nalloc)) == NULL)
601 return ERROR_INT("new ptr array not returned", __func__, 1);
602
603 sela->nalloc = 2 * sela->nalloc;
604 return 0;
605 }
606
607
608
609 /*----------------------------------------------------------------------*
610 * Accessors *
611 *----------------------------------------------------------------------*/
612 /*!
613 * \brief selaGetCount()
614 *
615 * \param[in] sela
616 * \return count, or 0 on error
617 */
618 l_int32
619 selaGetCount(SELA *sela)
620 {
621 if (!sela)
622 return ERROR_INT("sela not defined", __func__, 0);
623
624 return sela->n;
625 }
626
627
628 /*!
629 * \brief selaGetSel()
630 *
631 * \param[in] sela
632 * \param[in] i index of sel to be retrieved not copied
633 * \return sel, or NULL on error
634 *
635 * <pre>
636 * Notes:
637 * (1) This returns a ptr to the sel, not a copy, so the caller
638 * must not destroy it!
639 * </pre>
640 */
641 SEL *
642 selaGetSel(SELA *sela,
643 l_int32 i)
644 {
645 if (!sela)
646 return (SEL *)ERROR_PTR("sela not defined", __func__, NULL);
647
648 if (i < 0 || i >= sela->n)
649 return (SEL *)ERROR_PTR("invalid index", __func__, NULL);
650 return sela->sel[i];
651 }
652
653
654 /*!
655 * \brief selGetName()
656 *
657 * \param[in] sel
658 * \return sel name not copied, or NULL if no name or on error
659 */
660 char *
661 selGetName(SEL *sel)
662 {
663 if (!sel)
664 return (char *)ERROR_PTR("sel not defined", __func__, NULL);
665
666 return sel->name;
667 }
668
669
670 /*!
671 * \brief selSetName()
672 *
673 * \param[in] sel
674 * \param[in] name [optional]; can be null
675 * \return 0 if OK, 1 on error
676 *
677 * <pre>
678 * Notes:
679 * (1) Always frees the existing sel name, if defined.
680 * (2) If name is not defined, just clears any existing sel name.
681 * </pre>
682 */
683 l_ok
684 selSetName(SEL *sel,
685 const char *name)
686 {
687 if (!sel)
688 return ERROR_INT("sel not defined", __func__, 1);
689
690 return stringReplace(&sel->name, name);
691 }
692
693
694 /*!
695 * \brief selaFindSelByName()
696 *
697 * \param[in] sela
698 * \param[in] name sel name
699 * \param[out] pindex [optional]
700 * \param[in] psel [optional] sel (not a copy)
701 * \return 0 if OK; 1 on error
702 */
703 l_ok
704 selaFindSelByName(SELA *sela,
705 const char *name,
706 l_int32 *pindex,
707 SEL **psel)
708 {
709 l_int32 i, n;
710 char *sname;
711 SEL *sel;
712
713 if (pindex) *pindex = -1;
714 if (psel) *psel = NULL;
715
716 if (!sela)
717 return ERROR_INT("sela not defined", __func__, 1);
718
719 n = selaGetCount(sela);
720 for (i = 0; i < n; i++)
721 {
722 if ((sel = selaGetSel(sela, i)) == NULL) {
723 L_WARNING("missing sel\n", __func__);
724 continue;
725 }
726
727 sname = selGetName(sel);
728 if (sname && (!strcmp(name, sname))) {
729 if (pindex)
730 *pindex = i;
731 if (psel)
732 *psel = sel;
733 return 0;
734 }
735 }
736
737 return 1;
738 }
739
740
741 /*!
742 * \brief selGetElement()
743 *
744 * \param[in] sel
745 * \param[in] row
746 * \param[in] col
747 * \param[out] ptype SEL_HIT, SEL_MISS, SEL_DONT_CARE
748 * \return 0 if OK; 1 on error
749 */
750 l_ok
751 selGetElement(SEL *sel,
752 l_int32 row,
753 l_int32 col,
754 l_int32 *ptype)
755 {
756 if (!ptype)
757 return ERROR_INT("&type not defined", __func__, 1);
758 *ptype = SEL_DONT_CARE;
759 if (!sel)
760 return ERROR_INT("sel not defined", __func__, 1);
761 if (row < 0 || row >= sel->sy)
762 return ERROR_INT("sel row out of bounds", __func__, 1);
763 if (col < 0 || col >= sel->sx)
764 return ERROR_INT("sel col out of bounds", __func__, 1);
765
766 *ptype = sel->data[row][col];
767 return 0;
768 }
769
770
771 /*!
772 * \brief selSetElement()
773 *
774 * \param[in] sel
775 * \param[in] row
776 * \param[in] col
777 * \param[in] type SEL_HIT, SEL_MISS, SEL_DONT_CARE
778 * \return 0 if OK; 1 on error
779 *
780 * <pre>
781 * Notes:
782 * (1) Because we use row and column to index into an array,
783 * they are always non-negative. The location of the origin
784 * (and the type of operation) determine the actual
785 * direction of the rasterop.
786 * </pre>
787 */
788 l_ok
789 selSetElement(SEL *sel,
790 l_int32 row,
791 l_int32 col,
792 l_int32 type)
793 {
794 if (!sel)
795 return ERROR_INT("sel not defined", __func__, 1);
796 if (type != SEL_HIT && type != SEL_MISS && type != SEL_DONT_CARE)
797 return ERROR_INT("invalid sel element type", __func__, 1);
798 if (row < 0 || row >= sel->sy)
799 return ERROR_INT("sel row out of bounds", __func__, 1);
800 if (col < 0 || col >= sel->sx)
801 return ERROR_INT("sel col out of bounds", __func__, 1);
802
803 sel->data[row][col] = type;
804 return 0;
805 }
806
807
808 /*!
809 * \brief selGetParameters()
810 *
811 * \param[in] sel
812 * \param[out] psy, psx, pcy, pcx [optional] each can be null
813 * \return 0 if OK, 1 on error
814 */
815 l_ok
816 selGetParameters(SEL *sel,
817 l_int32 *psy,
818 l_int32 *psx,
819 l_int32 *pcy,
820 l_int32 *pcx)
821 {
822 if (psy) *psy = 0;
823 if (psx) *psx = 0;
824 if (pcy) *pcy = 0;
825 if (pcx) *pcx = 0;
826 if (!sel)
827 return ERROR_INT("sel not defined", __func__, 1);
828 if (psy) *psy = sel->sy;
829 if (psx) *psx = sel->sx;
830 if (pcy) *pcy = sel->cy;
831 if (pcx) *pcx = sel->cx;
832 return 0;
833 }
834
835
836 /*!
837 * \brief selSetOrigin()
838 *
839 * \param[in] sel
840 * \param[in] cy, cx
841 * \return 0 if OK; 1 on error
842 */
843 l_ok
844 selSetOrigin(SEL *sel,
845 l_int32 cy,
846 l_int32 cx)
847 {
848 if (!sel)
849 return ERROR_INT("sel not defined", __func__, 1);
850 sel->cy = cy;
851 sel->cx = cx;
852 return 0;
853 }
854
855
856 /*!
857 * \brief selGetTypeAtOrigin()
858 *
859 * \param[in] sel
860 * \param[out] ptype SEL_HIT, SEL_MISS, SEL_DONT_CARE
861 * \return 0 if OK; 1 on error or if origin is not found
862 */
863 l_ok
864 selGetTypeAtOrigin(SEL *sel,
865 l_int32 *ptype)
866 {
867 l_int32 sx, sy, cx, cy, i, j;
868
869 if (!ptype)
870 return ERROR_INT("&type not defined", __func__, 1);
871 *ptype = SEL_DONT_CARE; /* init */
872 if (!sel)
873 return ERROR_INT("sel not defined", __func__, 1);
874
875 selGetParameters(sel, &sy, &sx, &cy, &cx);
876 for (i = 0; i < sy; i++) {
877 for (j = 0; j < sx; j++) {
878 if (i == cy && j == cx) {
879 selGetElement(sel, i, j, ptype);
880 return 0;
881 }
882 }
883 }
884
885 return ERROR_INT("sel origin not found", __func__, 1);
886 }
887
888
889 /*!
890 * \brief selaGetBrickName()
891 *
892 * \param[in] sela
893 * \param[in] hsize, vsize of brick sel
894 * \return sel name new string, or NULL if no name or on error
895 */
896 char *
897 selaGetBrickName(SELA *sela,
898 l_int32 hsize,
899 l_int32 vsize)
900 {
901 l_int32 i, nsels, sx, sy;
902 SEL *sel;
903
904 if (!sela)
905 return (char *)ERROR_PTR("sela not defined", __func__, NULL);
906
907 nsels = selaGetCount(sela);
908 for (i = 0; i < nsels; i++) {
909 sel = selaGetSel(sela, i);
910 selGetParameters(sel, &sy, &sx, NULL, NULL);
911 if (hsize == sx && vsize == sy)
912 return stringNew(selGetName(sel));
913 }
914
915 return (char *)ERROR_PTR("sel not found", __func__, NULL);
916 }
917
918
919 /*!
920 * \brief selaGetCombName()
921 *
922 * \param[in] sela
923 * \param[in] size the product of sizes of the brick and comb parts
924 * \param[in] direction L_HORIZ, L_VERT
925 * \return sel name new string, or NULL if name not found or on error
926 *
927 * <pre>
928 * Notes:
929 * (1) Combs are by definition 1-dimensional, either horiz or vert.
930 * (2) Use this with comb Sels; e.g., from selaAddDwaCombs().
931 * </pre>
932 */
933 char *
934 selaGetCombName(SELA *sela,
935 l_int32 size,
936 l_int32 direction)
937 {
938 char *selname = NULL;
939 char combname[256];
940 l_int32 i, nsels, sx, sy, found;
941 SEL *sel;
942
943 if (!sela)
944 return (char *)ERROR_PTR("sela not defined", __func__, NULL);
945 if (direction != L_HORIZ && direction != L_VERT)
946 return (char *)ERROR_PTR("invalid direction", __func__, NULL);
947
948 /* Derive the comb name we're looking for */
949 if (direction == L_HORIZ)
950 snprintf(combname, sizeof(combname), "sel_comb_%dh", size);
951 else /* direction == L_VERT */
952 snprintf(combname, sizeof(combname), "sel_comb_%dv", size);
953
954 found = FALSE;
955 nsels = selaGetCount(sela);
956 for (i = 0; i < nsels; i++) {
957 sel = selaGetSel(sela, i);
958 selGetParameters(sel, &sy, &sx, NULL, NULL);
959 if (sy != 1 && sx != 1) /* 2-D; not a comb */
960 continue;
961 selname = selGetName(sel);
962 if (!strcmp(selname, combname)) {
963 found = TRUE;
964 break;
965 }
966 }
967
968 if (found)
969 return stringNew(selname);
970 else
971 return (char *)ERROR_PTR("sel not found", __func__, NULL);
972 }
973
974
975 /* --------- Function used to generate code in this file ---------- */
976 #if 0
977 static void selaComputeCompositeParameters(const char *fileout);
978
979 /*!
980 * \brief selaComputeCompParameters()
981 *
982 * \param[in] fileout
983 * \return void
984 *
985 * <pre>
986 * Notes:
987 * (1) This static function was used to construct the comp_parameter_map[]
988 * array at the top of this file. It is static because it does
989 * not need to be called again. It remains here to show how
990 * the composite parameter map was computed.
991 * (2) The output file was pasted directly into comp_parameter_map[].
992 * The composite parameter map is used to quickly determine
993 * the linear decomposition parameters and sel names.
994 * </pre>
995 */
996 static void
997 selaComputeCompositeParameters(const char *fileout)
998 {
999 char *str, *nameh1, *nameh2, *namev1, *namev2;
1000 char buf[256];
1001 l_int32 size, size1, size2, len;
1002 SARRAY *sa;
1003 SELA *selabasic, *selacomb;
1004
1005 selabasic = selaAddBasic(NULL);
1006 selacomb = selaAddDwaCombs(NULL);
1007 sa = sarrayCreate(64);
1008 for (size = 2; size < 64; size++) {
1009 selectComposableSizes(size, &size1, &size2);
1010 nameh1 = selaGetBrickName(selabasic, size1, 1);
1011 namev1 = selaGetBrickName(selabasic, 1, size1);
1012 if (size2 > 1) {
1013 nameh2 = selaGetCombName(selacomb, size1 * size2, L_HORIZ);
1014 namev2 = selaGetCombName(selacomb, size1 * size2, L_VERT);
1015 } else {
1016 nameh2 = stringNew("");
1017 namev2 = stringNew("");
1018 }
1019 snprintf(buf, sizeof(buf),
1020 " { %d, %d, %d, \"%s\", \"%s\", \"%s\", \"%s\" },",
1021 size, size1, size2, nameh1, nameh2, namev1, namev2);
1022 sarrayAddString(sa, buf, L_COPY);
1023 LEPT_FREE(nameh1);
1024 LEPT_FREE(nameh2);
1025 LEPT_FREE(namev1);
1026 LEPT_FREE(namev2);
1027 }
1028 str = sarrayToString(sa, 1);
1029 len = strlen(str);
1030 l_binaryWrite(fileout, "w", str, len + 1);
1031 LEPT_FREE(str);
1032 sarrayDestroy(&sa);
1033 selaDestroy(&selabasic);
1034 selaDestroy(&selacomb);
1035 }
1036 #endif
1037 /* -------------------------------------------------------------------- */
1038
1039
1040 /*!
1041 * \brief getCompositeParameters()
1042 *
1043 * \param[in] size
1044 * \param[out] psize1 [optional] brick factor size
1045 * \param[out] psize2 [optional] comb factor size
1046 * \param[out] pnameh1 [optional] name of horiz brick
1047 * \param[out] pnameh2 [optional] name of horiz comb
1048 * \param[out] pnamev1 [optional] name of vert brick
1049 * \param[out] pnamev2 [optional] name of vert comb
1050 * \return 0 if OK, 1 on error
1051 *
1052 * <pre>
1053 * Notes:
1054 * (1) This uses the big lookup table at the top of this file.
1055 * (2) All returned strings are copies that must be freed.
1056 * </pre>
1057 */
1058 l_ok
1059 getCompositeParameters(l_int32 size,
1060 l_int32 *psize1,
1061 l_int32 *psize2,
1062 char **pnameh1,
1063 char **pnameh2,
1064 char **pnamev1,
1065 char **pnamev2)
1066 {
1067 l_int32 index;
1068
1069 if (psize1) *psize1 = 0;
1070 if (psize2) *psize2 = 0;
1071 if (pnameh1) *pnameh1 = NULL;
1072 if (pnameh2) *pnameh2 = NULL;
1073 if (pnamev1) *pnamev1 = NULL;
1074 if (pnamev2) *pnamev2 = NULL;
1075 if (size < 2 || size > 63)
1076 return ERROR_INT("valid size range is {2 ... 63}", __func__, 1);
1077 index = size - 2;
1078 if (psize1)
1079 *psize1 = comp_parameter_map[index].size1;
1080 if (psize2)
1081 *psize2 = comp_parameter_map[index].size2;
1082 if (pnameh1)
1083 *pnameh1 = stringNew(comp_parameter_map[index].selnameh1);
1084 if (pnameh2)
1085 *pnameh2 = stringNew(comp_parameter_map[index].selnameh2);
1086 if (pnamev1)
1087 *pnamev1 = stringNew(comp_parameter_map[index].selnamev1);
1088 if (pnamev2)
1089 *pnamev2 = stringNew(comp_parameter_map[index].selnamev2);
1090 return 0;
1091 }
1092
1093
1094 /*!
1095 * \brief selaGetSelnames()
1096 *
1097 * \param[in] sela
1098 * \return sa of all sel names, or NULL on error
1099 */
1100 SARRAY *
1101 selaGetSelnames(SELA *sela)
1102 {
1103 char *selname;
1104 l_int32 i, n;
1105 SEL *sel;
1106 SARRAY *sa;
1107
1108 if (!sela)
1109 return (SARRAY *)ERROR_PTR("sela not defined", __func__, NULL);
1110 if ((n = selaGetCount(sela)) == 0)
1111 return (SARRAY *)ERROR_PTR("no sels in sela", __func__, NULL);
1112
1113 if ((sa = sarrayCreate(n)) == NULL)
1114 return (SARRAY *)ERROR_PTR("sa not made", __func__, NULL);
1115 for (i = 0; i < n; i++) {
1116 sel = selaGetSel(sela, i);
1117 selname = selGetName(sel);
1118 sarrayAddString(sa, selname, L_COPY);
1119 }
1120
1121 return sa;
1122 }
1123
1124
1125
1126 /*----------------------------------------------------------------------*
1127 * Max translations for erosion and hmt *
1128 *----------------------------------------------------------------------*/
1129 /*!
1130 * \brief selFindMaxTranslations()
1131 *
1132 * \param[in] sel
1133 * \param[out] pxp, pyp, pxn, pyn max shifts
1134 * \return 0 if OK; 1 on error
1135 *
1136 * <pre>
1137 * Notes:
1138 These are the maximum shifts for the erosion operation.
1139 * For example, when j < cx, the shift of the image
1140 * is +x to the cx. This is a positive xp shift.
1141 * </pre>
1142 */
1143 l_ok
1144 selFindMaxTranslations(SEL *sel,
1145 l_int32 *pxp,
1146 l_int32 *pyp,
1147 l_int32 *pxn,
1148 l_int32 *pyn)
1149 {
1150 l_int32 sx, sy, cx, cy, i, j;
1151 l_int32 maxxp, maxyp, maxxn, maxyn;
1152
1153 if (!pxp || !pyp || !pxn || !pyn)
1154 return ERROR_INT("&xp (etc) defined", __func__, 1);
1155 *pxp = *pyp = *pxn = *pyn = 0;
1156 if (!sel)
1157 return ERROR_INT("sel not defined", __func__, 1);
1158 selGetParameters(sel, &sy, &sx, &cy, &cx);
1159
1160 maxxp = maxyp = maxxn = maxyn = 0;
1161 for (i = 0; i < sy; i++) {
1162 for (j = 0; j < sx; j++) {
1163 if (sel->data[i][j] == 1) {
1164 maxxp = L_MAX(maxxp, cx - j);
1165 maxyp = L_MAX(maxyp, cy - i);
1166 maxxn = L_MAX(maxxn, j - cx);
1167 maxyn = L_MAX(maxyn, i - cy);
1168 }
1169 }
1170 }
1171
1172 *pxp = maxxp;
1173 *pyp = maxyp;
1174 *pxn = maxxn;
1175 *pyn = maxyn;
1176
1177 return 0;
1178 }
1179
1180
1181 /*----------------------------------------------------------------------*
1182 * Rotation by multiples of 90 degrees *
1183 *----------------------------------------------------------------------*/
1184 /*!
1185 * \brief selRotateOrth()
1186 *
1187 * \param[in] sel
1188 * \param[in] quads 0 - 4; number of 90 degree cw rotations
1189 * \return seld, or NULL on error
1190 */
1191 SEL *
1192 selRotateOrth(SEL *sel,
1193 l_int32 quads)
1194 {
1195 l_int32 i, j, ni, nj, sx, sy, cx, cy, nsx, nsy, ncx, ncy, type;
1196 SEL *seld;
1197
1198 if (!sel)
1199 return (SEL *)ERROR_PTR("sel not defined", __func__, NULL);
1200 if (quads < 0 || quads > 4)
1201 return (SEL *)ERROR_PTR("quads not in {0,1,2,3,4}", __func__, NULL);
1202 if (quads == 0 || quads == 4)
1203 return selCopy(sel);
1204
1205 selGetParameters(sel, &sy, &sx, &cy, &cx);
1206 if (quads == 1) { /* 90 degrees cw */
1207 nsx = sy;
1208 nsy = sx;
1209 ncx = sy - cy - 1;
1210 ncy = cx;
1211 } else if (quads == 2) { /* 180 degrees cw */
1212 nsx = sx;
1213 nsy = sy;
1214 ncx = sx - cx - 1;
1215 ncy = sy - cy - 1;
1216 } else { /* 270 degrees cw */
1217 nsx = sy;
1218 nsy = sx;
1219 ncx = cy;
1220 ncy = sx - cx - 1;
1221 }
1222 seld = selCreateBrick(nsy, nsx, ncy, ncx, SEL_DONT_CARE);
1223 if (sel->name)
1224 seld->name = stringNew(sel->name);
1225
1226 for (i = 0; i < sy; i++) {
1227 for (j = 0; j < sx; j++) {
1228 selGetElement(sel, i, j, &type);
1229 if (quads == 1) {
1230 ni = j;
1231 nj = sy - i - 1;
1232 } else if (quads == 2) {
1233 ni = sy - i - 1;
1234 nj = sx - j - 1;
1235 } else { /* quads == 3 */
1236 ni = sx - j - 1;
1237 nj = i;
1238 }
1239 selSetElement(seld, ni, nj, type);
1240 }
1241 }
1242
1243 return seld;
1244 }
1245
1246
1247 /*----------------------------------------------------------------------*
1248 * Sela and Sel serialized I/O *
1249 *----------------------------------------------------------------------*/
1250 /*!
1251 * \brief selaRead()
1252 *
1253 * \param[in] fname filename
1254 * \return sela, or NULL on error
1255 */
1256 SELA *
1257 selaRead(const char *fname)
1258 {
1259 FILE *fp;
1260 SELA *sela;
1261
1262 if (!fname)
1263 return (SELA *)ERROR_PTR("fname not defined", __func__, NULL);
1264
1265 if ((fp = fopenReadStream(fname)) == NULL)
1266 return (SELA *)ERROR_PTR_1("stream not opened", fname, __func__, NULL);
1267 if ((sela = selaReadStream(fp)) == NULL) {
1268 fclose(fp);
1269 return (SELA *)ERROR_PTR_1("sela not returned", fname, __func__, NULL);
1270 }
1271 fclose(fp);
1272
1273 return sela;
1274 }
1275
1276
1277 /*!
1278 * \brief selaReadStream()
1279 *
1280 * \param[in] fp file stream
1281 * \return sela, or NULL on error
1282 */
1283 SELA *
1284 selaReadStream(FILE *fp)
1285 {
1286 l_int32 i, n, version;
1287 SEL *sel;
1288 SELA *sela;
1289
1290 if (!fp)
1291 return (SELA *)ERROR_PTR("stream not defined", __func__, NULL);
1292
1293 if (fscanf(fp, "\nSela Version %d\n", &version) != 1)
1294 return (SELA *)ERROR_PTR("not a sela file", __func__, NULL);
1295 if (version != SEL_VERSION_NUMBER)
1296 return (SELA *)ERROR_PTR("invalid sel version", __func__, NULL);
1297 if (fscanf(fp, "Number of Sels = %d\n\n", &n) != 1)
1298 return (SELA *)ERROR_PTR("not a sela file", __func__, NULL);
1299
1300 if ((sela = selaCreate(n)) == NULL)
1301 return (SELA *)ERROR_PTR("sela not made", __func__, NULL);
1302 sela->nalloc = n;
1303
1304 for (i = 0; i < n; i++) {
1305 if ((sel = selReadStream(fp)) == NULL) {
1306 selaDestroy(&sela);
1307 return (SELA *)ERROR_PTR("sel not read", __func__, NULL);
1308 }
1309 selaAddSel(sela, sel, NULL, 0);
1310 }
1311
1312 return sela;
1313 }
1314
1315
1316 /*!
1317 * \brief selRead()
1318 *
1319 * \param[in] fname filename
1320 * \return sel, or NULL on error
1321 */
1322 SEL *
1323 selRead(const char *fname)
1324 {
1325 FILE *fp;
1326 SEL *sel;
1327
1328 if (!fname)
1329 return (SEL *)ERROR_PTR("fname not defined", __func__, NULL);
1330
1331 if ((fp = fopenReadStream(fname)) == NULL)
1332 return (SEL *)ERROR_PTR_1("stream not opened", fname, __func__, NULL);
1333 if ((sel = selReadStream(fp)) == NULL) {
1334 fclose(fp);
1335 return (SEL *)ERROR_PTR_1("sela not returned", fname, __func__, NULL);
1336 }
1337 fclose(fp);
1338
1339 return sel;
1340 }
1341
1342
1343 /*!
1344 * \brief selReadStream()
1345 *
1346 * \param[in] fp file stream
1347 * \return sel, or NULL on error
1348 */
1349 SEL *
1350 selReadStream(FILE *fp)
1351 {
1352 char selname[256];
1353 char linebuf[256];
1354 l_int32 sy, sx, cy, cx, i, j, version, ignore;
1355 SEL *sel;
1356
1357 if (!fp)
1358 return (SEL *)ERROR_PTR("stream not defined", __func__, NULL);
1359
1360 if (fscanf(fp, " Sel Version %d\n", &version) != 1)
1361 return (SEL *)ERROR_PTR("not a sel file", __func__, NULL);
1362 if (version != SEL_VERSION_NUMBER)
1363 return (SEL *)ERROR_PTR("invalid sel version", __func__, NULL);
1364
1365 if (fgets(linebuf, sizeof(linebuf), fp) == NULL)
1366 return (SEL *)ERROR_PTR("error reading into linebuf", __func__, NULL);
1367 sscanf(linebuf, " ------ %200s ------", selname);
1368
1369 if (fscanf(fp, " sy = %d, sx = %d, cy = %d, cx = %d\n",
1370 &sy, &sx, &cy, &cx) != 4)
1371 return (SEL *)ERROR_PTR("dimensions not read", __func__, NULL);
1372
1373 if ((sel = selCreate(sy, sx, selname)) == NULL)
1374 return (SEL *)ERROR_PTR("sel not made", __func__, NULL);
1375 selSetOrigin(sel, cy, cx);
1376
1377 for (i = 0; i < sy; i++) {
1378 ignore = fscanf(fp, " ");
1379 for (j = 0; j < sx; j++)
1380 ignore = fscanf(fp, "%1d", &sel->data[i][j]);
1381 ignore = fscanf(fp, "\n");
1382 }
1383 ignore = fscanf(fp, "\n");
1384
1385 return sel;
1386 }
1387
1388
1389 /*!
1390 * \brief selaWrite()
1391 *
1392 * \param[in] fname filename
1393 * \param[in] sela
1394 * \return 0 if OK, 1 on error
1395 */
1396 l_ok
1397 selaWrite(const char *fname,
1398 SELA *sela)
1399 {
1400 FILE *fp;
1401
1402 if (!fname)
1403 return ERROR_INT("fname not defined", __func__, 1);
1404 if (!sela)
1405 return ERROR_INT("sela not defined", __func__, 1);
1406
1407 if ((fp = fopenWriteStream(fname, "wb")) == NULL)
1408 return ERROR_INT_1("stream not opened", fname, __func__, 1);
1409 selaWriteStream(fp, sela);
1410 fclose(fp);
1411
1412 return 0;
1413 }
1414
1415
1416 /*!
1417 * \brief selaWriteStream()
1418 *
1419 * \param[in] fp file stream
1420 * \param[in] sela
1421 * \return 0 if OK, 1 on error
1422 */
1423 l_ok
1424 selaWriteStream(FILE *fp,
1425 SELA *sela)
1426 {
1427 l_int32 i, n;
1428 SEL *sel;
1429
1430 if (!fp)
1431 return ERROR_INT("stream not defined", __func__, 1);
1432 if (!sela)
1433 return ERROR_INT("sela not defined", __func__, 1);
1434
1435 n = selaGetCount(sela);
1436 fprintf(fp, "\nSela Version %d\n", SEL_VERSION_NUMBER);
1437 fprintf(fp, "Number of Sels = %d\n\n", n);
1438 for (i = 0; i < n; i++) {
1439 if ((sel = selaGetSel(sela, i)) == NULL)
1440 continue;
1441 selWriteStream(fp, sel);
1442 }
1443 return 0;
1444 }
1445
1446
1447 /*!
1448 * \brief selWrite()
1449 *
1450 * \param[in] fname filename
1451 * \param[in] sel
1452 * \return 0 if OK, 1 on error
1453 */
1454 l_ok
1455 selWrite(const char *fname,
1456 SEL *sel)
1457 {
1458 FILE *fp;
1459
1460 if (!fname)
1461 return ERROR_INT("fname not defined", __func__, 1);
1462 if (!sel)
1463 return ERROR_INT("sel not defined", __func__, 1);
1464
1465 if ((fp = fopenWriteStream(fname, "wb")) == NULL)
1466 return ERROR_INT_1("stream not opened", fname, __func__, 1);
1467 selWriteStream(fp, sel);
1468 fclose(fp);
1469
1470 return 0;
1471 }
1472
1473
1474 /*!
1475 * \brief selWriteStream()
1476 *
1477 * \param[in] fp file stream
1478 * \param[in] sel
1479 * \return 0 if OK, 1 on error
1480 */
1481 l_ok
1482 selWriteStream(FILE *fp,
1483 SEL *sel)
1484 {
1485 l_int32 sx, sy, cx, cy, i, j;
1486
1487 if (!fp)
1488 return ERROR_INT("stream not defined", __func__, 1);
1489 if (!sel)
1490 return ERROR_INT("sel not defined", __func__, 1);
1491 selGetParameters(sel, &sy, &sx, &cy, &cx);
1492
1493 fprintf(fp, " Sel Version %d\n", SEL_VERSION_NUMBER);
1494 fprintf(fp, " ------ %s ------\n", selGetName(sel));
1495 fprintf(fp, " sy = %d, sx = %d, cy = %d, cx = %d\n", sy, sx, cy, cx);
1496 for (i = 0; i < sy; i++) {
1497 fprintf(fp, " ");
1498 for (j = 0; j < sx; j++)
1499 fprintf(fp, "%d", sel->data[i][j]);
1500 fprintf(fp, "\n");
1501 }
1502 fprintf(fp, "\n");
1503
1504 return 0;
1505 }
1506
1507
1508 /*----------------------------------------------------------------------*
1509 * Building custom hit-miss sels from compiled strings *
1510 *----------------------------------------------------------------------*/
1511 /*!
1512 * \brief selCreateFromString()
1513 *
1514 * \param[in] text
1515 * \param[in] h, w height, width
1516 * \param[in] name [optional] sel name; can be null
1517 * \return sel of the given size, or NULL on error
1518 *
1519 * <pre>
1520 * Notes:
1521 * (1) The text is an array of chars (in row-major order) where
1522 * each char can be one of the following:
1523 * 'x': hit
1524 * 'o': miss
1525 * ' ': don't-care
1526 * (2) When the origin falls on a hit or miss, use an upper case
1527 * char (e.g., 'X' or 'O') to indicate it. When the origin
1528 * falls on a don't-care, indicate this with a 'C'.
1529 * The string must have exactly one origin specified.
1530 * (3) The advantage of this method is that the text can be input
1531 * in a format that shows the 2D layout of the Sel; e.g.,
1532 * \code
1533 * static const char *seltext = "x "
1534 * "x Oo "
1535 * "x "
1536 * "xxxxx";
1537 * \endcode
1538 * </pre>
1539 */
1540 SEL *
1541 selCreateFromString(const char *text,
1542 l_int32 h,
1543 l_int32 w,
1544 const char *name)
1545 {
1546 SEL *sel;
1547 l_int32 y, x, norig;
1548 char ch;
1549
1550 if (!text || text[0] == '\0')
1551 return (SEL *)ERROR_PTR("text undefined or empty", __func__, NULL);
1552 if (h < 1)
1553 return (SEL *)ERROR_PTR("height must be > 0", __func__, NULL);
1554 if (w < 1)
1555 return (SEL *)ERROR_PTR("width must be > 0", __func__, NULL);
1556 if (strlen(text) != (size_t)w * h)
1557 return (SEL *)ERROR_PTR("text size != w * h", __func__, NULL);
1558
1559 sel = selCreate(h, w, name);
1560 norig = 0;
1561 for (y = 0; y < h; ++y) {
1562 for (x = 0; x < w; ++x) {
1563 ch = *(text++);
1564 switch (ch)
1565 {
1566 case 'X':
1567 norig++;
1568 selSetOrigin(sel, y, x);
1569 /* fall through */
1570 case 'x':
1571 selSetElement(sel, y, x, SEL_HIT);
1572 break;
1573
1574 case 'O':
1575 norig++;
1576 selSetOrigin(sel, y, x);
1577 /* fall through */
1578 case 'o':
1579 selSetElement(sel, y, x, SEL_MISS);
1580 break;
1581
1582 case 'C':
1583 norig++;
1584 selSetOrigin(sel, y, x);
1585 /* fall through */
1586 case ' ':
1587 selSetElement(sel, y, x, SEL_DONT_CARE);
1588 break;
1589
1590 case '\n':
1591 /* ignored */
1592 continue;
1593
1594 default:
1595 selDestroy(&sel);
1596 return (SEL *)ERROR_PTR("unknown char", __func__, NULL);
1597 }
1598 }
1599 }
1600 if (norig != 1) {
1601 L_ERROR("Exactly one origin must be specified; this string has %d\n",
1602 __func__, norig);
1603 selDestroy(&sel);
1604 }
1605
1606 return sel;
1607 }
1608
1609
1610 /*!
1611 * \brief selPrintToString()
1612 *
1613 * \param[in] sel
1614 * \return str string; caller must free
1615 *
1616 * <pre>
1617 * Notes:
1618 * (1) This is an inverse function of selCreateFromString.
1619 * It prints a textual representation of the SEL to a malloc'd
1620 * string. The format is the same as selCreateFromString
1621 * except that newlines are inserted into the output
1622 * between rows.
1623 * (2) This is useful for debugging. However, if you want to
1624 * save some Sels in a file, put them in a Sela and write
1625 * them out with selaWrite(). They can then be read in
1626 * with selaRead().
1627 * </pre>
1628 */
1629 char *
1630 selPrintToString(SEL *sel)
1631 {
1632 char is_center;
1633 char *str, *strptr;
1634 l_int32 type;
1635 l_int32 sx, sy, cx, cy, x, y;
1636
1637 if (!sel)
1638 return (char *)ERROR_PTR("sel not defined", __func__, NULL);
1639
1640 selGetParameters(sel, &sy, &sx, &cy, &cx);
1641 if ((str = (char *)LEPT_CALLOC(1, sy * (sx + 1) + 1)) == NULL)
1642 return (char *)ERROR_PTR("calloc fail for str", __func__, NULL);
1643 strptr = str;
1644
1645 for (y = 0; y < sy; ++y) {
1646 for (x = 0; x < sx; ++x) {
1647 selGetElement(sel, y, x, &type);
1648 is_center = (x == cx && y == cy);
1649 switch (type) {
1650 case SEL_HIT:
1651 *(strptr++) = is_center ? 'X' : 'x';
1652 break;
1653 case SEL_MISS:
1654 *(strptr++) = is_center ? 'O' : 'o';
1655 break;
1656 case SEL_DONT_CARE:
1657 *(strptr++) = is_center ? 'C' : ' ';
1658 break;
1659 }
1660 }
1661 *(strptr++) = '\n';
1662 }
1663
1664 return str;
1665 }
1666
1667
1668 /*----------------------------------------------------------------------*
1669 * Building custom hit-miss sels from a simple file format *
1670 *----------------------------------------------------------------------*/
1671 /*!
1672 * \brief selaCreateFromFile()
1673 *
1674 * \param[in] filename
1675 * \return sela, or NULL on error
1676 *
1677 * <pre>
1678 * Notes:
1679 * (1) The file contains a sequence of Sel descriptions.
1680 * (2) Each Sel is formatted as follows:
1681 * ~ Any number of comment lines starting with '#' are ignored
1682 * ~ The next line contains the selname
1683 * ~ The next lines contain the Sel data. They must be
1684 * formatted similarly to the string format in
1685 * selCreateFromString(), with each line beginning and
1686 * ending with a double-quote, and showing the 2D layout.
1687 * ~ Each Sel ends when a blank line, a comment line, or
1688 * the end of file is reached.
1689 * (3) See selCreateFromString() for a description of the string
1690 * format for the Sel data. As an example, here are the lines
1691 * of is a valid file for a single Sel. In the file, all lines
1692 * are left-justified:
1693 * # diagonal sel
1694 * sel_5diag
1695 * "x "
1696 * " x "
1697 * " X "
1698 * " x "
1699 * " x"
1700 * </pre>
1701 */
1702 SELA *
1703 selaCreateFromFile(const char *filename)
1704 {
1705 char *filestr, *line;
1706 l_int32 i, n, first, last, nsel, insel;
1707 size_t nbytes;
1708 NUMA *nafirst, *nalast;
1709 SARRAY *sa;
1710 SEL *sel;
1711 SELA *sela;
1712
1713 if (!filename)
1714 return (SELA *)ERROR_PTR("filename not defined", __func__, NULL);
1715
1716 filestr = (char *)l_binaryRead(filename, &nbytes);
1717 sa = sarrayCreateLinesFromString(filestr, 1);
1718 LEPT_FREE(filestr);
1719 n = sarrayGetCount(sa);
1720 sela = selaCreate(0);
1721
1722 /* Find the start and end lines for each Sel.
1723 * We allow the "blank" lines to be null strings or
1724 * to have standard whitespace (' ','\t',\'n') or be '#'. */
1725 nafirst = numaCreate(0);
1726 nalast = numaCreate(0);
1727 insel = FALSE;
1728 for (i = 0; i < n; i++) {
1729 line = sarrayGetString(sa, i, L_NOCOPY);
1730 if (!insel &&
1731 (line[0] != '\0' && line[0] != ' ' &&
1732 line[0] != '\t' && line[0] != '\n' && line[0] != '#')) {
1733 numaAddNumber(nafirst, i);
1734 insel = TRUE;
1735 continue;
1736 }
1737 if (insel &&
1738 (line[0] == '\0' || line[0] == ' ' ||
1739 line[0] == '\t' || line[0] == '\n' || line[0] == '#')) {
1740 numaAddNumber(nalast, i - 1);
1741 insel = FALSE;
1742 continue;
1743 }
1744 }
1745 if (insel) /* fell off the end of the file */
1746 numaAddNumber(nalast, n - 1);
1747
1748 /* Extract sels */
1749 nsel = numaGetCount(nafirst);
1750 for (i = 0; i < nsel; i++) {
1751 numaGetIValue(nafirst, i, &first);
1752 numaGetIValue(nalast, i, &last);
1753 if ((sel = selCreateFromSArray(sa, first, last)) == NULL) {
1754 lept_stderr("Error reading sel from %d to %d\n", first, last);
1755 selaDestroy(&sela);
1756 sarrayDestroy(&sa);
1757 numaDestroy(&nafirst);
1758 numaDestroy(&nalast);
1759 return (SELA *)ERROR_PTR("bad sela file", __func__, NULL);
1760 }
1761 selaAddSel(sela, sel, NULL, 0);
1762 }
1763
1764 numaDestroy(&nafirst);
1765 numaDestroy(&nalast);
1766 sarrayDestroy(&sa);
1767 return sela;
1768 }
1769
1770
1771 /*!
1772 * \brief selCreateFromSArray()
1773 *
1774 * \param[in] sa
1775 * \param[in] first line of sarray where Sel begins
1776 * \param[in] last line of sarray where Sel ends
1777 * \return sela, or NULL on error
1778 *
1779 * <pre>
1780 * Notes:
1781 * (1) The Sel contains the following lines:
1782 * ~ The first line is the selname
1783 * ~ The remaining lines contain the Sel data. They must
1784 * be formatted similarly to the string format in
1785 * selCreateFromString(), with each line beginning and
1786 * ending with a double-quote, and showing the 2D layout.
1787 * ~ 'last' gives the last line in the Sel data.
1788 * (2) See selCreateFromString() for a description of the string
1789 * format for the Sel data. As an example, here are the lines
1790 * of is a valid file for a single Sel. In the file, all lines
1791 * are left-justified:
1792 * # diagonal sel
1793 * sel_5diag
1794 * "x "
1795 * " x "
1796 * " X "
1797 * " x "
1798 * " x"
1799 * </pre>
1800 */
1801 static SEL *
1802 selCreateFromSArray(SARRAY *sa,
1803 l_int32 first,
1804 l_int32 last)
1805 {
1806 char ch;
1807 char *name, *line;
1808 l_int32 n, len, i, w, h, y, x;
1809 SEL *sel;
1810
1811 if (!sa)
1812 return (SEL *)ERROR_PTR("sa not defined", __func__, NULL);
1813 n = sarrayGetCount(sa);
1814 if (first < 0 || first >= n || last <= first || last >= n)
1815 return (SEL *)ERROR_PTR("invalid range", __func__, NULL);
1816
1817 name = sarrayGetString(sa, first, L_NOCOPY);
1818 h = last - first;
1819 line = sarrayGetString(sa, first + 1, L_NOCOPY);
1820 len = strlen(line);
1821 if (line[0] != '"' || line[len - 1] != '"')
1822 return (SEL *)ERROR_PTR("invalid format", __func__, NULL);
1823 w = len - 2;
1824 if ((sel = selCreate(h, w, name)) == NULL)
1825 return (SEL *)ERROR_PTR("sel not made", __func__, NULL);
1826 for (i = first + 1; i <= last; i++) {
1827 line = sarrayGetString(sa, i, L_NOCOPY);
1828 y = i - first - 1;
1829 for (x = 0; x < w; ++x) {
1830 ch = line[x + 1]; /* skip the leading double-quote */
1831 switch (ch)
1832 {
1833 case 'X':
1834 selSetOrigin(sel, y, x); /* set origin and hit */
1835 /* fall through */
1836 case 'x':
1837 selSetElement(sel, y, x, SEL_HIT);
1838 break;
1839
1840 case 'O':
1841 selSetOrigin(sel, y, x); /* set origin and miss */
1842 /* fall through */
1843 case 'o':
1844 selSetElement(sel, y, x, SEL_MISS);
1845 break;
1846
1847 case 'C':
1848 selSetOrigin(sel, y, x); /* set origin and don't-care */
1849 /* fall through */
1850 case ' ':
1851 selSetElement(sel, y, x, SEL_DONT_CARE);
1852 break;
1853
1854 default:
1855 selDestroy(&sel);
1856 return (SEL *)ERROR_PTR("unknown char", __func__, NULL);
1857 }
1858 }
1859 }
1860
1861 return sel;
1862 }
1863
1864
1865 /*----------------------------------------------------------------------*
1866 * Making hit-only SELs from Pta and Pix *
1867 *----------------------------------------------------------------------*/
1868 /*!
1869 * \brief selCreateFromPta()
1870 *
1871 * \param[in] pta
1872 * \param[in] cy, cx origin of sel
1873 * \param[in] name [optional] sel name; can be null
1874 * \return sel of minimum required size, or NULL on error
1875 *
1876 * <pre>
1877 * Notes:
1878 * (1) The origin and all points in the pta must be positive.
1879 * </pre>
1880 */
1881 SEL *
1882 selCreateFromPta(PTA *pta,
1883 l_int32 cy,
1884 l_int32 cx,
1885 const char *name)
1886 {
1887 l_int32 i, n, x, y, w, h;
1888 BOX *box;
1889 SEL *sel;
1890
1891 if (!pta)
1892 return (SEL *)ERROR_PTR("pta not defined", __func__, NULL);
1893 if (cy < 0 || cx < 0)
1894 return (SEL *)ERROR_PTR("(cy, cx) not both >= 0", __func__, NULL);
1895 n = ptaGetCount(pta);
1896 if (n == 0)
1897 return (SEL *)ERROR_PTR("no pts in pta", __func__, NULL);
1898
1899 box = ptaGetBoundingRegion(pta);
1900 boxGetGeometry(box, &x, &y, &w, &h);
1901 boxDestroy(&box);
1902 if (x < 0 || y < 0)
1903 return (SEL *)ERROR_PTR("not all x and y >= 0", __func__, NULL);
1904
1905 sel = selCreate(y + h, x + w, name);
1906 selSetOrigin(sel, cy, cx);
1907 for (i = 0; i < n; i++) {
1908 ptaGetIPt(pta, i, &x, &y);
1909 selSetElement(sel, y, x, SEL_HIT);
1910 }
1911
1912 return sel;
1913 }
1914
1915
1916 /*!
1917 * \brief selCreateFromPix()
1918 *
1919 * \param[in] pix
1920 * \param[in] cy, cx origin of sel
1921 * \param[in] name [optional] sel name; can be null
1922 * \return sel, or NULL on error
1923 *
1924 * <pre>
1925 * Notes:
1926 * (1) The origin must be positive.
1927 * (2) The pix must not exceed MaxPixTemplateSize in either dimension.
1928 * and the total number of hits must not exceed MaxPixTemplateHits.
1929 * </pre>
1930 */
1931 SEL *
1932 selCreateFromPix(PIX *pix,
1933 l_int32 cy,
1934 l_int32 cx,
1935 const char *name)
1936 {
1937 SEL *sel;
1938 l_int32 i, j, w, h, d, nhits;
1939 l_uint32 val;
1940
1941 if (!pix)
1942 return (SEL *)ERROR_PTR("pix not defined", __func__, NULL);
1943 if (cy < 0 || cx < 0)
1944 return (SEL *)ERROR_PTR("(cy, cx) not both >= 0", __func__, NULL);
1945 pixGetDimensions(pix, &w, &h, &d);
1946 if (d != 1)
1947 return (SEL *)ERROR_PTR("pix not 1 bpp", __func__, NULL);
1948 if (w > MaxPixTemplateSize || h > MaxPixTemplateSize) {
1949 L_ERROR("pix template too large (w = %d, h = %d)\n", __func__, w, h);
1950 return NULL;
1951 }
1952 if (w > MaxPixTemplateSize / 5 || h > MaxPixTemplateSize / 5)
1953 L_WARNING("large pix template: w = %d, h = %d\n", __func__, w, h);
1954 pixCountPixels(pix, &nhits, NULL);
1955 if (nhits > MaxPixTemplateHits) {
1956 L_ERROR("too many hits (%d) in pix template\n", __func__, nhits);
1957 return NULL;
1958 }
1959 if (nhits > MaxPixTemplateHits / 5)
1960 L_WARNING("many hits (%d) in pix template\n", __func__, nhits);
1961
1962 sel = selCreate(h, w, name);
1963 selSetOrigin(sel, cy, cx);
1964 for (i = 0; i < h; i++) {
1965 for (j = 0; j < w; j++) {
1966 pixGetPixel(pix, j, i, &val);
1967 if (val)
1968 selSetElement(sel, i, j, SEL_HIT);
1969 }
1970 }
1971
1972 return sel;
1973 }
1974
1975
1976 /*----------------------------------------------------------------------*
1977 * Making hit-miss sels from color Pix and image files *
1978 *----------------------------------------------------------------------*/
1979 /*!
1980 *
1981 * selReadFromColorImage()
1982 *
1983 * \param[in] pathname
1984 * \return sel if OK; NULL on error
1985 *
1986 * <pre>
1987 * Notes:
1988 * (1) Loads an image from a file and creates a (hit-miss) sel.
1989 * (2) The sel name is taken from the pathname without the directory
1990 * and extension.
1991 * </pre>
1992 */
1993 SEL *
1994 selReadFromColorImage(const char *pathname)
1995 {
1996 PIX *pix;
1997 SEL *sel;
1998 char *basename, *selname;
1999
2000 splitPathAtExtension (pathname, &basename, NULL);
2001 splitPathAtDirectory (basename, NULL, &selname);
2002 LEPT_FREE(basename);
2003
2004 if ((pix = pixRead(pathname)) == NULL) {
2005 LEPT_FREE(selname);
2006 return (SEL *)ERROR_PTR("pix not returned", __func__, NULL);
2007 }
2008 if ((sel = selCreateFromColorPix(pix, selname)) == NULL)
2009 L_ERROR("sel not made\n", __func__);
2010
2011 LEPT_FREE(selname);
2012 pixDestroy(&pix);
2013 return sel;
2014 }
2015
2016
2017 /*!
2018 *
2019 * selCreateFromColorPix()
2020 *
2021 * \param[in] pixs cmapped or rgb
2022 * \param[in] selname [optional] sel name; can be null
2023 * \return sel if OK, NULL on error
2024 *
2025 * <pre>
2026 * Notes:
2027 * (1) The sel size is given by the size of pixs.
2028 * (2) In pixs, hits are represented by green pixels, misses by red
2029 * pixels, and don't-cares by white pixels.
2030 * (3) In pixs, there may be no misses, but there must be at least 1 hit.
2031 * (4) At most there can be only one origin pixel, which is optionally
2032 * specified by using a lower-intensity pixel:
2033 * if a hit: dark green
2034 * if a miss: dark red
2035 * if a don't care: gray
2036 * If there is no such pixel, the origin defaults to the approximate
2037 * center of the sel.
2038 * </pre>
2039 */
2040 SEL *
2041 selCreateFromColorPix(PIX *pixs,
2042 const char *selname)
2043 {
2044 PIXCMAP *cmap;
2045 SEL *sel;
2046 l_int32 hascolor, num_origins, nohits;
2047 l_int32 w, h, d, i, j, red, green, blue;
2048 l_uint32 pixval;
2049
2050 if (!pixs)
2051 return (SEL *)ERROR_PTR("pixs not defined", __func__, NULL);
2052
2053 hascolor = FALSE;
2054 cmap = pixGetColormap(pixs);
2055 if (cmap)
2056 pixcmapHasColor(cmap, &hascolor);
2057 pixGetDimensions(pixs, &w, &h, &d);
2058 if (hascolor == FALSE && d != 32)
2059 return (SEL *)ERROR_PTR("pixs has no color", __func__, NULL);
2060
2061 if ((sel = selCreate (h, w, NULL)) == NULL)
2062 return (SEL *)ERROR_PTR ("sel not made", __func__, NULL);
2063 selSetOrigin (sel, h / 2, w / 2); /* default */
2064 selSetName(sel, selname);
2065
2066 num_origins = 0;
2067 nohits = TRUE;
2068 for (i = 0; i < h; i++) {
2069 for (j = 0; j < w; j++) {
2070 pixGetPixel (pixs, j, i, &pixval);
2071
2072 if (cmap) {
2073 pixcmapGetColor (cmap, pixval, &red, &green, &blue);
2074 } else {
2075 red = GET_DATA_BYTE (&pixval, COLOR_RED);
2076 green = GET_DATA_BYTE (&pixval, COLOR_GREEN);
2077 blue = GET_DATA_BYTE (&pixval, COLOR_BLUE);
2078 }
2079
2080 if (red < 255 && green < 255 && blue < 255) {
2081 num_origins++;
2082 if (num_origins == 1) /* first one found */
2083 selSetOrigin (sel, i, j);
2084 if (num_origins == 2)
2085 L_WARNING("multiple origins in sel image\n", __func__);
2086 }
2087 if (!red && green && !blue) {
2088 nohits = FALSE;
2089 selSetElement (sel, i, j, SEL_HIT);
2090 } else if (red && !green && !blue) {
2091 selSetElement (sel, i, j, SEL_MISS);
2092 } else if (red && green && blue) {
2093 selSetElement (sel, i, j, SEL_DONT_CARE);
2094 } else {
2095 selDestroy(&sel);
2096 return (SEL *)ERROR_PTR("invalid color", __func__, NULL);
2097 }
2098 }
2099 }
2100
2101 if (nohits) {
2102 selDestroy(&sel);
2103 return (SEL *)ERROR_PTR("no hits in sel", __func__, NULL);
2104 }
2105 return sel;
2106 }
2107
2108
2109 /*!
2110 *
2111 * selaCreateFromColorPixa()
2112 *
2113 * \param[in] pixa color pixa representing the sels
2114 * \param[in] sa sarray of sel names
2115 * \return sel if OK, NULL on error
2116 *
2117 * <pre>
2118 * Notes:
2119 * (1) See notes in selCreateFromColorPix()
2120 * (2) sa is required because all sels that are put in a sela
2121 * must have a name.
2122 * </pre>
2123 */
2124 SELA *
2125 selaCreateFromColorPixa(PIXA *pixa,
2126 SARRAY *sa)
2127 {
2128 char *str;
2129 l_int32 i, n;
2130 PIX *pix;
2131 SEL *sel;
2132 SELA *sela;
2133
2134 if (!pixa)
2135 return (SELA *)ERROR_PTR("pixa not defined", __func__, NULL);
2136 if (!sa)
2137 return (SELA *)ERROR_PTR("sa of sel names not defined", __func__, NULL);
2138
2139 n = pixaGetCount(pixa);
2140 if ((sela = selaCreate(n)) == NULL)
2141 return (SELA *)ERROR_PTR("sela not allocated", __func__, NULL);
2142 for (i = 0; i < n; i++) {
2143 pix = pixaGetPix(pixa, i, L_CLONE);
2144 str = sarrayGetString(sa, i, L_NOCOPY);
2145 sel = selCreateFromColorPix(pix, str);
2146 selaAddSel(sela, sel, NULL, L_INSERT);
2147 pixDestroy(&pix);
2148 }
2149 return sela;
2150 }
2151
2152
2153 /*----------------------------------------------------------------------*
2154 * Printable display of sel *
2155 *----------------------------------------------------------------------*/
2156 /*!
2157 * \brief selDisplayInPix()
2158 *
2159 * \param[in] sel
2160 * \param[in] size of grid interiors; odd; minimum size of 13 is enforced
2161 * \param[in] gthick grid thickness; minimum size of 2 is enforced
2162 * \return pix display of sel, or NULL on error
2163 *
2164 * <pre>
2165 * Notes:
2166 * (1) This gives a visual representation of a general (hit-miss) sel.
2167 * (2) The empty sel is represented by a grid of intersecting lines.
2168 * (3) Three different patterns are generated for the sel elements:
2169 * ~ hit (solid black circle)
2170 * ~ miss (black ring; inner radius is radius2)
2171 * ~ origin (cross, XORed with whatever is there)
2172 * </pre>
2173 */
2174 PIX *
2175 selDisplayInPix(SEL *sel,
2176 l_int32 size,
2177 l_int32 gthick)
2178 {
2179 l_int32 i, j, w, h, sx, sy, cx, cy, type, width;
2180 l_int32 radius1, radius2, shift1, shift2, x0, y0;
2181 PIX *pixd, *pix2, *pixh, *pixm, *pixorig;
2182 PTA *pta1, *pta2, *pta1t, *pta2t;
2183
2184 if (!sel)
2185 return (PIX *)ERROR_PTR("sel not defined", __func__, NULL);
2186 if (size < 13) {
2187 L_WARNING("size < 13; setting to 13\n", __func__);
2188 size = 13;
2189 }
2190 if (size % 2 == 0)
2191 size++;
2192 if (gthick < 2) {
2193 L_WARNING("grid thickness < 2; setting to 2\n", __func__);
2194 gthick = 2;
2195 }
2196 selGetParameters(sel, &sy, &sx, &cy, &cx);
2197 w = size * sx + gthick * (sx + 1);
2198 h = size * sy + gthick * (sy + 1);
2199 pixd = pixCreate(w, h, 1);
2200
2201 /* Generate grid lines */
2202 for (i = 0; i <= sy; i++)
2203 pixRenderLine(pixd, 0, gthick / 2 + i * (size + gthick),
2204 w - 1, gthick / 2 + i * (size + gthick),
2205 gthick, L_SET_PIXELS);
2206 for (j = 0; j <= sx; j++)
2207 pixRenderLine(pixd, gthick / 2 + j * (size + gthick), 0,
2208 gthick / 2 + j * (size + gthick), h - 1,
2209 gthick, L_SET_PIXELS);
2210
2211 /* Generate hit and miss patterns */
2212 radius1 = (l_int32)(0.85 * ((size - 1) / 2.0) + 0.5); /* of hit */
2213 radius2 = (l_int32)(0.65 * ((size - 1) / 2.0) + 0.5); /* of inner miss */
2214 pta1 = generatePtaFilledCircle(radius1);
2215 pta2 = generatePtaFilledCircle(radius2);
2216 shift1 = (size - 1) / 2 - radius1; /* center circle in square */
2217 shift2 = (size - 1) / 2 - radius2;
2218 pta1t = ptaTransform(pta1, shift1, shift1, 1.0, 1.0);
2219 pta2t = ptaTransform(pta2, shift2, shift2, 1.0, 1.0);
2220 pixh = pixGenerateFromPta(pta1t, size, size); /* hits */
2221 pix2 = pixGenerateFromPta(pta2t, size, size);
2222 pixm = pixSubtract(NULL, pixh, pix2);
2223
2224 /* Generate crossed lines for origin pattern */
2225 pixorig = pixCreate(size, size, 1);
2226 width = size / 8;
2227 pixRenderLine(pixorig, size / 2, (l_int32)(0.12 * size),
2228 size / 2, (l_int32)(0.88 * size),
2229 width, L_SET_PIXELS);
2230 pixRenderLine(pixorig, (l_int32)(0.15 * size), size / 2,
2231 (l_int32)(0.85 * size), size / 2,
2232 width, L_FLIP_PIXELS);
2233 pixRasterop(pixorig, size / 2 - width, size / 2 - width,
2234 2 * width, 2 * width, PIX_NOT(PIX_DST), NULL, 0, 0);
2235
2236 /* Specialize origin pattern for this sel */
2237 selGetTypeAtOrigin(sel, &type);
2238 if (type == SEL_HIT)
2239 pixXor(pixorig, pixorig, pixh);
2240 else if (type == SEL_MISS)
2241 pixXor(pixorig, pixorig, pixm);
2242
2243 /* Paste the patterns in */
2244 y0 = gthick;
2245 for (i = 0; i < sy; i++) {
2246 x0 = gthick;
2247 for (j = 0; j < sx; j++) {
2248 selGetElement(sel, i, j, &type);
2249 if (i == cy && j == cx) /* origin */
2250 pixRasterop(pixd, x0, y0, size, size, PIX_SRC, pixorig, 0, 0);
2251 else if (type == SEL_HIT)
2252 pixRasterop(pixd, x0, y0, size, size, PIX_SRC, pixh, 0, 0);
2253 else if (type == SEL_MISS)
2254 pixRasterop(pixd, x0, y0, size, size, PIX_SRC, pixm, 0, 0);
2255 x0 += size + gthick;
2256 }
2257 y0 += size + gthick;
2258 }
2259
2260 pixDestroy(&pix2);
2261 pixDestroy(&pixh);
2262 pixDestroy(&pixm);
2263 pixDestroy(&pixorig);
2264 ptaDestroy(&pta1);
2265 ptaDestroy(&pta1t);
2266 ptaDestroy(&pta2);
2267 ptaDestroy(&pta2t);
2268 return pixd;
2269 }
2270
2271
2272 /*!
2273 * \brief selaDisplayInPix()
2274 *
2275 * \param[in] sela
2276 * \param[in] size of grid interiors; odd; minimum size of 13 is enforced
2277 * \param[in] gthick grid thickness; minimum size of 2 is enforced
2278 * \param[in] spacing between sels, both horizontally and vertically
2279 * \param[in] ncols number of sels per "line"
2280 * \return pix display of all sels in sela, or NULL on error
2281 *
2282 * <pre>
2283 * Notes:
2284 * (1) This gives a visual representation of all the sels in a sela.
2285 * (2) See notes in selDisplayInPix() for display params of each sel.
2286 * (3) This gives the nicest results when all sels in the sela
2287 * are the same size.
2288 * </pre>
2289 */
2290 PIX *
2291 selaDisplayInPix(SELA *sela,
2292 l_int32 size,
2293 l_int32 gthick,
2294 l_int32 spacing,
2295 l_int32 ncols)
2296 {
2297 l_int32 nsels, i, w, width;
2298 PIX *pixt, *pixd;
2299 PIXA *pixa;
2300 SEL *sel;
2301
2302 if (!sela)
2303 return (PIX *)ERROR_PTR("sela not defined", __func__, NULL);
2304 if (size < 13) {
2305 L_WARNING("size < 13; setting to 13\n", __func__);
2306 size = 13;
2307 }
2308 if (size % 2 == 0)
2309 size++;
2310 if (gthick < 2) {
2311 L_WARNING("grid thickness < 2; setting to 2\n", __func__);
2312 gthick = 2;
2313 }
2314 if (spacing < 5) {
2315 L_WARNING("spacing < 5; setting to 5\n", __func__);
2316 spacing = 5;
2317 }
2318
2319 /* Accumulate the pix of each sel */
2320 nsels = selaGetCount(sela);
2321 pixa = pixaCreate(nsels);
2322 for (i = 0; i < nsels; i++) {
2323 sel = selaGetSel(sela, i);
2324 pixt = selDisplayInPix(sel, size, gthick);
2325 pixaAddPix(pixa, pixt, L_INSERT);
2326 }
2327
2328 /* Find the tiled output width, using just the first
2329 * ncols pix in the pixa. If all pix have the same width,
2330 * they will align properly in columns. */
2331 width = 0;
2332 ncols = L_MIN(nsels, ncols);
2333 for (i = 0; i < ncols; i++) {
2334 pixt = pixaGetPix(pixa, i, L_CLONE);
2335 pixGetDimensions(pixt, &w, NULL, NULL);
2336 width += w;
2337 pixDestroy(&pixt);
2338 }
2339 width += (ncols + 1) * spacing; /* add spacing all around as well */
2340
2341 pixd = pixaDisplayTiledInRows(pixa, 1, width, 1.0, 0, spacing, 0);
2342 pixaDestroy(&pixa);
2343 return pixd;
2344 }