comparison mupdf-source/thirdparty/leptonica/src/sel2.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 * \file sel2.c
29 * <pre>
30 *
31 * Contains definitions of simple structuring elements
32 *
33 * Basic brick structuring elements
34 * SELA *selaAddBasic()
35 * Linear horizontal and vertical
36 * Square
37 * Diagonals
38 *
39 * Simple hit-miss structuring elements
40 * SELA *selaAddHitMiss()
41 * Isolated foreground pixel
42 * Horizontal and vertical edges
43 * Slanted edge
44 * Corners
45 *
46 * Structuring elements for comparing with DWA operations
47 * SELA *selaAddDwaLinear()
48 * SELA *selaAddDwaCombs()
49 *
50 * Structuring elements for the intersection of lines
51 * SELA *selaAddCrossJunctions()
52 * SELA *selaAddTJunctions()
53 *
54 * Structuring elements for connectivity-preserving thinning operations
55 * SELA *sela4ccThin()
56 * SELA *sela8ccThin()
57 * SELA *sela4and8ccThin()
58 *
59 * Other structuring elements
60 * SEL *selMakePlusSign()
61 * </pre>
62 */
63
64 #ifdef HAVE_CONFIG_H
65 #include <config_auto.h>
66 #endif /* HAVE_CONFIG_H */
67
68 #include <math.h>
69 #include "allheaders.h"
70
71 #define L_BUF_SIZE 512
72
73 /* Linear brick sel sizes, including all those that are required
74 * for decomposable sels up to size 63. */
75 static const l_int32 num_linear = 25;
76 static const l_int32 basic_linear[] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
77 12, 13, 14, 15, 20, 21, 25, 30, 31, 35, 40, 41, 45, 50, 51};
78
79
80 /* ------------------------------------------------------------------- *
81 * Basic brick structuring elements *
82 * ------------------------------------------------------------------- */
83 /*!
84 * \brief selaAddBasic()
85 *
86 * \param[in] sela [optional]
87 * \return sela with additional sels, or NULL on error
88 *
89 * <pre>
90 * Notes:
91 * (1) Adds the following sels:
92 * ~ all linear (horiz, vert) brick sels that are
93 * necessary for decomposable sels up to size 63
94 * ~ square brick sels up to size 10
95 * ~ 4 diagonal sels
96 * </pre>
97 */
98 SELA *
99 selaAddBasic(SELA *sela)
100 {
101 char name[L_BUF_SIZE];
102 l_int32 i, size;
103 SEL *sel;
104
105 if (!sela) {
106 if ((sela = selaCreate(0)) == NULL)
107 return (SELA *)ERROR_PTR("sela not made", __func__, NULL);
108 }
109
110 /*--------------------------------------------------------------*
111 * Linear horizontal and vertical sels *
112 *--------------------------------------------------------------*/
113 for (i = 0; i < num_linear; i++) {
114 size = basic_linear[i];
115 sel = selCreateBrick(1, size, 0, size / 2, 1);
116 snprintf(name, L_BUF_SIZE, "sel_%dh", size);
117 selaAddSel(sela, sel, name, 0);
118 }
119 for (i = 0; i < num_linear; i++) {
120 size = basic_linear[i];
121 sel = selCreateBrick(size, 1, size / 2, 0, 1);
122 snprintf(name, L_BUF_SIZE, "sel_%dv", size);
123 selaAddSel(sela, sel, name, 0);
124 }
125
126 /*-----------------------------------------------------------*
127 * 2-d Bricks *
128 *-----------------------------------------------------------*/
129 for (i = 2; i <= 5; i++) {
130 sel = selCreateBrick(i, i, i / 2, i / 2, 1);
131 snprintf(name, L_BUF_SIZE, "sel_%d", i);
132 selaAddSel(sela, sel, name, 0);
133 }
134
135 /*-----------------------------------------------------------*
136 * Diagonals *
137 *-----------------------------------------------------------*/
138 /* 0c 1
139 1 0 */
140 sel = selCreateBrick(2, 2, 0, 0, 1);
141 selSetElement(sel, 0, 0, 0);
142 selSetElement(sel, 1, 1, 0);
143 selaAddSel(sela, sel, "sel_2dp", 0);
144
145 /* 1c 0
146 0 1 */
147 sel = selCreateBrick(2, 2, 0, 0, 1);
148 selSetElement(sel, 0, 1, 0);
149 selSetElement(sel, 1, 0, 0);
150 selaAddSel(sela, sel, "sel_2dm", 0);
151
152 /* Diagonal, slope +, size 5 */
153 sel = selCreate(5, 5, "sel_5dp");
154 selSetOrigin(sel, 2, 2);
155 selSetElement(sel, 0, 4, 1);
156 selSetElement(sel, 1, 3, 1);
157 selSetElement(sel, 2, 2, 1);
158 selSetElement(sel, 3, 1, 1);
159 selSetElement(sel, 4, 0, 1);
160 selaAddSel(sela, sel, "sel_5dp", 0);
161
162 /* Diagonal, slope -, size 5 */
163 sel = selCreate(5, 5, "sel_5dm");
164 selSetOrigin(sel, 2, 2);
165 selSetElement(sel, 0, 0, 1);
166 selSetElement(sel, 1, 1, 1);
167 selSetElement(sel, 2, 2, 1);
168 selSetElement(sel, 3, 3, 1);
169 selSetElement(sel, 4, 4, 1);
170 selaAddSel(sela, sel, "sel_5dm", 0);
171
172 return sela;
173 }
174
175
176 /* ------------------------------------------------------------------- *
177 * Simple hit-miss structuring elements *
178 * ------------------------------------------------------------------- */
179 /*!
180 * \brief selaAddHitMiss()
181 *
182 * \param[in] sela [optional]
183 * \return sela with additional sels, or NULL on error
184 */
185 SELA *
186 selaAddHitMiss(SELA *sela)
187 {
188 SEL *sel;
189
190 if (!sela) {
191 if ((sela = selaCreate(0)) == NULL)
192 return (SELA *)ERROR_PTR("sela not made", __func__, NULL);
193 }
194
195 #if 0 /* use just for testing */
196 sel = selCreateBrick(3, 3, 1, 1, 2);
197 selaAddSel(sela, sel, "sel_bad", 0);
198 #endif
199
200
201 /*--------------------------------------------------------------*
202 * Isolated foreground pixel *
203 *--------------------------------------------------------------*/
204 sel = selCreateBrick(3, 3, 1, 1, SEL_MISS);
205 selSetElement(sel, 1, 1, SEL_HIT);
206 selaAddSel(sela, sel, "sel_3hm", 0);
207
208 /*--------------------------------------------------------------*
209 * Horizontal and vertical edges *
210 *--------------------------------------------------------------*/
211 sel = selCreateBrick(2, 3, 0, 1, SEL_HIT);
212 selSetElement(sel, 1, 0, SEL_MISS);
213 selSetElement(sel, 1, 1, SEL_MISS);
214 selSetElement(sel, 1, 2, SEL_MISS);
215 selaAddSel(sela, sel, "sel_3de", 0);
216
217 sel = selCreateBrick(2, 3, 1, 1, SEL_HIT);
218 selSetElement(sel, 0, 0, SEL_MISS);
219 selSetElement(sel, 0, 1, SEL_MISS);
220 selSetElement(sel, 0, 2, SEL_MISS);
221 selaAddSel(sela, sel, "sel_3ue", 0);
222
223 sel = selCreateBrick(3, 2, 1, 0, SEL_HIT);
224 selSetElement(sel, 0, 1, SEL_MISS);
225 selSetElement(sel, 1, 1, SEL_MISS);
226 selSetElement(sel, 2, 1, SEL_MISS);
227 selaAddSel(sela, sel, "sel_3re", 0);
228
229 sel = selCreateBrick(3, 2, 1, 1, SEL_HIT);
230 selSetElement(sel, 0, 0, SEL_MISS);
231 selSetElement(sel, 1, 0, SEL_MISS);
232 selSetElement(sel, 2, 0, SEL_MISS);
233 selaAddSel(sela, sel, "sel_3le", 0);
234
235 /*--------------------------------------------------------------*
236 * Slanted edge *
237 *--------------------------------------------------------------*/
238 sel = selCreateBrick(13, 6, 6, 2, SEL_DONT_CARE);
239 selSetElement(sel, 0, 3, SEL_MISS);
240 selSetElement(sel, 0, 5, SEL_HIT);
241 selSetElement(sel, 4, 2, SEL_MISS);
242 selSetElement(sel, 4, 4, SEL_HIT);
243 selSetElement(sel, 8, 1, SEL_MISS);
244 selSetElement(sel, 8, 3, SEL_HIT);
245 selSetElement(sel, 12, 0, SEL_MISS);
246 selSetElement(sel, 12, 2, SEL_HIT);
247 selaAddSel(sela, sel, "sel_sl1", 0);
248
249 /*--------------------------------------------------------------*
250 * Corners *
251 * This allows for up to 3 missing edge pixels at the corner *
252 *--------------------------------------------------------------*/
253 sel = selCreateBrick(4, 4, 1, 1, SEL_MISS);
254 selSetElement(sel, 1, 1, SEL_DONT_CARE);
255 selSetElement(sel, 1, 2, SEL_DONT_CARE);
256 selSetElement(sel, 2, 1, SEL_DONT_CARE);
257 selSetElement(sel, 1, 3, SEL_HIT);
258 selSetElement(sel, 2, 2, SEL_HIT);
259 selSetElement(sel, 2, 3, SEL_HIT);
260 selSetElement(sel, 3, 1, SEL_HIT);
261 selSetElement(sel, 3, 2, SEL_HIT);
262 selSetElement(sel, 3, 3, SEL_HIT);
263 selaAddSel(sela, sel, "sel_ulc", 0);
264
265 sel = selCreateBrick(4, 4, 1, 2, SEL_MISS);
266 selSetElement(sel, 1, 1, SEL_DONT_CARE);
267 selSetElement(sel, 1, 2, SEL_DONT_CARE);
268 selSetElement(sel, 2, 2, SEL_DONT_CARE);
269 selSetElement(sel, 1, 0, SEL_HIT);
270 selSetElement(sel, 2, 0, SEL_HIT);
271 selSetElement(sel, 2, 1, SEL_HIT);
272 selSetElement(sel, 3, 0, SEL_HIT);
273 selSetElement(sel, 3, 1, SEL_HIT);
274 selSetElement(sel, 3, 2, SEL_HIT);
275 selaAddSel(sela, sel, "sel_urc", 0);
276
277 sel = selCreateBrick(4, 4, 2, 1, SEL_MISS);
278 selSetElement(sel, 1, 1, SEL_DONT_CARE);
279 selSetElement(sel, 2, 1, SEL_DONT_CARE);
280 selSetElement(sel, 2, 2, SEL_DONT_CARE);
281 selSetElement(sel, 0, 1, SEL_HIT);
282 selSetElement(sel, 0, 2, SEL_HIT);
283 selSetElement(sel, 0, 3, SEL_HIT);
284 selSetElement(sel, 1, 2, SEL_HIT);
285 selSetElement(sel, 1, 3, SEL_HIT);
286 selSetElement(sel, 2, 3, SEL_HIT);
287 selaAddSel(sela, sel, "sel_llc", 0);
288
289 sel = selCreateBrick(4, 4, 2, 2, SEL_MISS);
290 selSetElement(sel, 1, 2, SEL_DONT_CARE);
291 selSetElement(sel, 2, 1, SEL_DONT_CARE);
292 selSetElement(sel, 2, 2, SEL_DONT_CARE);
293 selSetElement(sel, 0, 0, SEL_HIT);
294 selSetElement(sel, 0, 1, SEL_HIT);
295 selSetElement(sel, 0, 2, SEL_HIT);
296 selSetElement(sel, 1, 0, SEL_HIT);
297 selSetElement(sel, 1, 1, SEL_HIT);
298 selSetElement(sel, 2, 0, SEL_HIT);
299 selaAddSel(sela, sel, "sel_lrc", 0);
300
301 return sela;
302 }
303
304
305 /* ------------------------------------------------------------------- *
306 * Structuring elements for comparing with DWA operations *
307 * ------------------------------------------------------------------- */
308 /*!
309 * \brief selaAddDwaLinear()
310 *
311 * \param[in] sela [optional]
312 * \return sela with additional sels, or NULL on error
313 *
314 * <pre>
315 * Notes:
316 * (1) Adds all linear (horizontal, vertical) sels from
317 * 2 to 63 pixels in length, which are the sizes over
318 * which dwa code can be generated.
319 * </pre>
320 */
321 SELA *
322 selaAddDwaLinear(SELA *sela)
323 {
324 char name[L_BUF_SIZE];
325 l_int32 i;
326 SEL *sel;
327
328 if (!sela) {
329 if ((sela = selaCreate(0)) == NULL)
330 return (SELA *)ERROR_PTR("sela not made", __func__, NULL);
331 }
332
333 for (i = 2; i < 64; i++) {
334 sel = selCreateBrick(1, i, 0, i / 2, 1);
335 snprintf(name, L_BUF_SIZE, "sel_%dh", i);
336 selaAddSel(sela, sel, name, 0);
337 }
338 for (i = 2; i < 64; i++) {
339 sel = selCreateBrick(i, 1, i / 2, 0, 1);
340 snprintf(name, L_BUF_SIZE, "sel_%dv", i);
341 selaAddSel(sela, sel, name, 0);
342 }
343 return sela;
344 }
345
346
347 /*!
348 * \brief selaAddDwaCombs()
349 *
350 * \param[in] sela [optional]
351 * \return sela with additional sels, or NULL on error
352 *
353 * <pre>
354 * Notes:
355 * (1) Adds all comb (horizontal, vertical) Sels that are
356 * used in composite linear morphological operations
357 * up to 63 pixels in length, which are the sizes over
358 * which dwa code can be generated.
359 * </pre>
360 */
361 SELA *
362 selaAddDwaCombs(SELA *sela)
363 {
364 char name[L_BUF_SIZE];
365 l_int32 i, f1, f2, prevsize, size;
366 SEL *selh, *selv;
367
368 if (!sela) {
369 if ((sela = selaCreate(0)) == NULL)
370 return (SELA *)ERROR_PTR("sela not made", __func__, NULL);
371 }
372
373 prevsize = 0;
374 for (i = 4; i < 64; i++) {
375 selectComposableSizes(i, &f1, &f2);
376 size = f1 * f2;
377 if (size == prevsize)
378 continue;
379 selectComposableSels(i, L_HORIZ, NULL, &selh);
380 if (selh) {
381 snprintf(name, L_BUF_SIZE, "sel_comb_%dh", size);
382 selaAddSel(sela, selh, name, 0);
383 } else {
384 L_ERROR("selh not made for i = %d\n", __func__, i);
385 }
386 selectComposableSels(i, L_VERT, NULL, &selv);
387 if (selv) {
388 snprintf(name, L_BUF_SIZE, "sel_comb_%dv", size);
389 selaAddSel(sela, selv, name, 0);
390 } else {
391 L_ERROR("selv not made for i = %d\n", __func__, i);
392 }
393 prevsize = size;
394 }
395
396 return sela;
397 }
398
399
400 /* ------------------------------------------------------------------- *
401 * Structuring elements for the intersection of lines *
402 * ------------------------------------------------------------------- */
403 /*!
404 * \brief selaAddCrossJunctions()
405 *
406 * \param[in] sela [optional]
407 * \param[in] hlsize length of each line of hits from origin
408 * \param[in] mdist distance of misses from the origin
409 * \param[in] norient number of orientations; max of 8
410 * \param[in] debugflag 1 for debug output
411 * \return sela with additional sels, or NULL on error
412 *
413 * <pre>
414 * Notes:
415 * (1) Adds hitmiss Sels for the intersection of two lines.
416 * If the lines are very thin, they must be nearly orthogonal
417 * to register.
418 * (2) The number of Sels generated is equal to %norient.
419 * (3) If %norient == 2, this generates 2 Sels of crosses, each with
420 * two perpendicular lines of hits. One Sel has horizontal and
421 * vertical hits; the other has hits along lines at +-45 degrees.
422 * Likewise, if %norient == 3, this generates 3 Sels of crosses
423 * oriented at 30 degrees with each other.
424 * (4) It is suggested that %hlsize be chosen at least 1 greater
425 * than %mdist. Try values of (%hlsize, %mdist) such as
426 * (6,5), (7,6), (8,7), (9,7), etc.
427 * </pre>
428 */
429 SELA *
430 selaAddCrossJunctions(SELA *sela,
431 l_float32 hlsize,
432 l_float32 mdist,
433 l_int32 norient,
434 l_int32 debugflag)
435 {
436 char name[L_BUF_SIZE];
437 l_int32 i, j, w, xc, yc;
438 l_float64 pi, halfpi, radincr, radang;
439 l_float64 angle;
440 PIX *pixc, *pixm, *pixt;
441 PIXA *pixa;
442 PTA *pta1, *pta2, *pta3, *pta4;
443 SEL *sel;
444
445 if (hlsize <= 0)
446 return (SELA *)ERROR_PTR("hlsize not > 0", __func__, NULL);
447 if (norient < 1 || norient > 8)
448 return (SELA *)ERROR_PTR("norient not in [1, ... 8]", __func__, NULL);
449
450 if (!sela) {
451 if ((sela = selaCreate(0)) == NULL)
452 return (SELA *)ERROR_PTR("sela not made", __func__, NULL);
453 }
454
455 pi = 3.1415926535;
456 halfpi = 3.1415926535 / 2.0;
457 radincr = halfpi / (l_float64)norient;
458 w = (l_int32)(2.2 * (L_MAX(hlsize, mdist) + 0.5));
459 if (w % 2 == 0)
460 w++;
461 xc = w / 2;
462 yc = w / 2;
463
464 pixa = pixaCreate(norient);
465 for (i = 0; i < norient; i++) {
466
467 /* Set the don't cares */
468 pixc = pixCreate(w, w, 32);
469 pixSetAll(pixc);
470
471 /* Add the green lines of hits */
472 pixm = pixCreate(w, w, 1);
473 radang = (l_float32)i * radincr;
474 pta1 = generatePtaLineFromPt(xc, yc, hlsize + 1, radang);
475 pta2 = generatePtaLineFromPt(xc, yc, hlsize + 1, radang + halfpi);
476 pta3 = generatePtaLineFromPt(xc, yc, hlsize + 1, radang + pi);
477 pta4 = generatePtaLineFromPt(xc, yc, hlsize + 1, radang + pi + halfpi);
478 ptaJoin(pta1, pta2, 0, -1);
479 ptaJoin(pta1, pta3, 0, -1);
480 ptaJoin(pta1, pta4, 0, -1);
481 pixRenderPta(pixm, pta1, L_SET_PIXELS);
482 pixPaintThroughMask(pixc, pixm, 0, 0, 0x00ff0000);
483 ptaDestroy(&pta1);
484 ptaDestroy(&pta2);
485 ptaDestroy(&pta3);
486 ptaDestroy(&pta4);
487
488 /* Add red misses between the lines */
489 for (j = 0; j < 4; j++) {
490 angle = radang + (j - 0.5) * halfpi;
491 pixSetPixel(pixc, xc + (l_int32)(mdist * cos(angle)),
492 yc + (l_int32)(mdist * sin(angle)), 0xff000000);
493 }
494
495 /* Add dark green for origin */
496 pixSetPixel(pixc, xc, yc, 0x00550000);
497
498 /* Generate the sel */
499 sel = selCreateFromColorPix(pixc, NULL);
500 snprintf(name, sizeof(name), "sel_cross_%d", i);
501 selaAddSel(sela, sel, name, 0);
502
503 if (debugflag) {
504 pixt = pixScaleBySampling(pixc, 10.0, 10.0);
505 pixaAddPix(pixa, pixt, L_INSERT);
506 }
507 pixDestroy(&pixm);
508 pixDestroy(&pixc);
509 }
510
511 if (debugflag) {
512 l_int32 w;
513 lept_mkdir("lept/sel");
514 pixaGetPixDimensions(pixa, 0, &w, NULL, NULL);
515 pixt = pixaDisplayTiledAndScaled(pixa, 32, w, 1, 0, 10, 2);
516 pixWriteDebug("/tmp/lept/sel/xsel1.png", pixt, IFF_PNG);
517 pixDisplay(pixt, 0, 100);
518 pixDestroy(&pixt);
519 pixt = selaDisplayInPix(sela, 15, 2, 20, 1);
520 pixWriteDebug("/tmp/lept/sel/xsel2.png", pixt, IFF_PNG);
521 pixDisplay(pixt, 500, 100);
522 pixDestroy(&pixt);
523 selaWriteStream(stderr, sela);
524 }
525 pixaDestroy(&pixa);
526
527 return sela;
528 }
529
530
531 /*!
532 * \brief selaAddTJunctions()
533 *
534 * \param[in] sela [optional]
535 * \param[in] hlsize length of each line of hits from origin
536 * \param[in] mdist distance of misses from the origin
537 * \param[in] norient number of orientations; max of 8
538 * \param[in] debugflag 1 for debug output
539 * \return sela with additional sels, or NULL on error
540 *
541 * <pre>
542 * Notes:
543 * (1) Adds hitmiss Sels for the T-junction of two lines.
544 * If the lines are very thin, they must be nearly orthogonal
545 * to register.
546 * (2) The number of Sels generated is 4 * %norient.
547 * (3) It is suggested that %hlsize be chosen at least 1 greater
548 * than %mdist. Try values of (%hlsize, %mdist) such as
549 * (6,5), (7,6), (8,7), (9,7), etc.
550 * </pre>
551 */
552 SELA *
553 selaAddTJunctions(SELA *sela,
554 l_float32 hlsize,
555 l_float32 mdist,
556 l_int32 norient,
557 l_int32 debugflag)
558 {
559 char name[L_BUF_SIZE];
560 l_int32 i, j, k, w, xc, yc;
561 l_float64 pi, halfpi, radincr, jang, radang;
562 l_float64 angle[3], dist[3];
563 PIX *pixc, *pixm, *pixt;
564 PIXA *pixa;
565 PTA *pta1, *pta2, *pta3;
566 SEL *sel;
567
568 if (hlsize <= 2)
569 return (SELA *)ERROR_PTR("hlsizel not > 1", __func__, NULL);
570 if (norient < 1 || norient > 8)
571 return (SELA *)ERROR_PTR("norient not in [1, ... 8]", __func__, NULL);
572
573 if (!sela) {
574 if ((sela = selaCreate(0)) == NULL)
575 return (SELA *)ERROR_PTR("sela not made", __func__, NULL);
576 }
577
578 pi = 3.1415926535;
579 halfpi = 3.1415926535 / 2.0;
580 radincr = halfpi / (l_float32)norient;
581 w = (l_int32)(2.4 * (L_MAX(hlsize, mdist) + 0.5));
582 if (w % 2 == 0)
583 w++;
584 xc = w / 2;
585 yc = w / 2;
586
587 pixa = pixaCreate(4 * norient);
588 for (i = 0; i < norient; i++) {
589 for (j = 0; j < 4; j++) { /* 4 orthogonal orientations */
590 jang = (l_float32)j * halfpi;
591
592 /* Set the don't cares */
593 pixc = pixCreate(w, w, 32);
594 pixSetAll(pixc);
595
596 /* Add the green lines of hits */
597 pixm = pixCreate(w, w, 1);
598 radang = (l_float32)i * radincr;
599 pta1 = generatePtaLineFromPt(xc, yc, hlsize + 1, jang + radang);
600 pta2 = generatePtaLineFromPt(xc, yc, hlsize + 1,
601 jang + radang + halfpi);
602 pta3 = generatePtaLineFromPt(xc, yc, hlsize + 1,
603 jang + radang + pi);
604 ptaJoin(pta1, pta2, 0, -1);
605 ptaJoin(pta1, pta3, 0, -1);
606 pixRenderPta(pixm, pta1, L_SET_PIXELS);
607 pixPaintThroughMask(pixc, pixm, 0, 0, 0x00ff0000);
608 ptaDestroy(&pta1);
609 ptaDestroy(&pta2);
610 ptaDestroy(&pta3);
611
612 /* Add red misses between the lines */
613 angle[0] = radang + jang - halfpi;
614 angle[1] = radang + jang + 0.5 * halfpi;
615 angle[2] = radang + jang + 1.5 * halfpi;
616 dist[0] = 0.8 * mdist;
617 dist[1] = dist[2] = mdist;
618 for (k = 0; k < 3; k++) {
619 pixSetPixel(pixc, xc + (l_int32)(dist[k] * cos(angle[k])),
620 yc + (l_int32)(dist[k] * sin(angle[k])),
621 0xff000000);
622 }
623
624 /* Add dark green for origin */
625 pixSetPixel(pixc, xc, yc, 0x00550000);
626
627 /* Generate the sel */
628 sel = selCreateFromColorPix(pixc, NULL);
629 snprintf(name, sizeof(name), "sel_cross_%d", 4 * i + j);
630 selaAddSel(sela, sel, name, 0);
631
632 if (debugflag) {
633 pixt = pixScaleBySampling(pixc, 10.0, 10.0);
634 pixaAddPix(pixa, pixt, L_INSERT);
635 }
636 pixDestroy(&pixm);
637 pixDestroy(&pixc);
638 }
639 }
640
641 if (debugflag) {
642 l_int32 w;
643 lept_mkdir("lept/sel");
644 pixaGetPixDimensions(pixa, 0, &w, NULL, NULL);
645 pixt = pixaDisplayTiledAndScaled(pixa, 32, w, 4, 0, 10, 2);
646 pixWriteDebug("/tmp/lept/sel/tsel1.png", pixt, IFF_PNG);
647 pixDisplay(pixt, 0, 100);
648 pixDestroy(&pixt);
649 pixt = selaDisplayInPix(sela, 15, 2, 20, 4);
650 pixWriteDebug("/tmp/lept/sel/tsel2.png", pixt, IFF_PNG);
651 pixDisplay(pixt, 500, 100);
652 pixDestroy(&pixt);
653 selaWriteStream(stderr, sela);
654 }
655 pixaDestroy(&pixa);
656
657 return sela;
658 }
659
660
661 /* -------------------------------------------------------------------------- *
662 * Structuring elements for connectivity-preserving thinning operations *
663 * -------------------------------------------------------------------------- */
664
665 /* ------------------------------------------------------------
666 * These sels (and their rotated counterparts) are the useful
667 * 3x3 Sels for thinning. The notation is based on
668 * "Connectivity-preserving morphological image transformations,"
669 * a version of which can be found at
670 * http://www.leptonica.com/papers/conn.pdf
671 * ------------------------------------------------------------ */
672
673 /* Sels for 4-connected thinning */
674 static const char *sel_4_1 = " x"
675 "oCx"
676 " x";
677 static const char *sel_4_2 = " x"
678 "oCx"
679 " o ";
680 static const char *sel_4_3 = " o "
681 "oCx"
682 " x";
683 static const char *sel_4_4 = " o "
684 "oCx"
685 " o ";
686 static const char *sel_4_5 = " ox"
687 "oCx"
688 " o ";
689 static const char *sel_4_6 = " o "
690 "oCx"
691 " ox";
692 static const char *sel_4_7 = " xx"
693 "oCx"
694 " o ";
695 static const char *sel_4_8 = " x"
696 "oCx"
697 "o x";
698 static const char *sel_4_9 = "o x"
699 "oCx"
700 " x";
701
702 /* Sels for 8-connected thinning */
703 static const char *sel_8_1 = " x "
704 "oCx"
705 " x ";
706 static const char *sel_8_2 = " x "
707 "oCx"
708 "o ";
709 static const char *sel_8_3 = "o "
710 "oCx"
711 " x ";
712 static const char *sel_8_4 = "o "
713 "oCx"
714 "o ";
715 static const char *sel_8_5 = "o x"
716 "oCx"
717 "o ";
718 static const char *sel_8_6 = "o "
719 "oCx"
720 "o x";
721 static const char *sel_8_7 = " x "
722 "oCx"
723 "oo ";
724 static const char *sel_8_8 = " x "
725 "oCx"
726 "ox ";
727 static const char *sel_8_9 = "ox "
728 "oCx"
729 " x ";
730
731 /* Sels for both 4 and 8-connected thinning */
732 static const char *sel_48_1 = " xx"
733 "oCx"
734 "oo ";
735 static const char *sel_48_2 = "o x"
736 "oCx"
737 "o x";
738
739
740 /*!
741 * \brief sela4ccThin()
742 *
743 * \param[in] sela [optional]
744 * \return sela with additional sels, or NULL on error
745 *
746 * <pre>
747 * Notes:
748 * (1) Adds the 9 basic sels for 4-cc thinning.
749 * </pre>
750 */
751 SELA *
752 sela4ccThin(SELA *sela)
753 {
754 SEL *sel;
755
756 if (!sela) sela = selaCreate(9);
757
758 sel = selCreateFromString(sel_4_1, 3, 3, "sel_4_1");
759 selaAddSel(sela, sel, NULL, 0);
760 sel = selCreateFromString(sel_4_2, 3, 3, "sel_4_2");
761 selaAddSel(sela, sel, NULL, 0);
762 sel = selCreateFromString(sel_4_3, 3, 3, "sel_4_3");
763 selaAddSel(sela, sel, NULL, 0);
764 sel = selCreateFromString(sel_4_4, 3, 3, "sel_4_4");
765 selaAddSel(sela, sel, NULL, 0);
766 sel = selCreateFromString(sel_4_5, 3, 3, "sel_4_5");
767 selaAddSel(sela, sel, NULL, 0);
768 sel = selCreateFromString(sel_4_6, 3, 3, "sel_4_6");
769 selaAddSel(sela, sel, NULL, 0);
770 sel = selCreateFromString(sel_4_7, 3, 3, "sel_4_7");
771 selaAddSel(sela, sel, NULL, 0);
772 sel = selCreateFromString(sel_4_8, 3, 3, "sel_4_8");
773 selaAddSel(sela, sel, NULL, 0);
774 sel = selCreateFromString(sel_4_9, 3, 3, "sel_4_9");
775 selaAddSel(sela, sel, NULL, 0);
776
777 return sela;
778 }
779
780
781 /*!
782 * \brief sela8ccThin()
783 *
784 * \param[in] sela [optional]
785 * \return sela with additional sels, or NULL on error
786 *
787 * <pre>
788 * Notes:
789 * (1) Adds the 9 basic sels for 8-cc thinning.
790 * </pre>
791 */
792 SELA *
793 sela8ccThin(SELA *sela)
794 {
795 SEL *sel;
796
797 if (!sela) sela = selaCreate(9);
798
799 sel = selCreateFromString(sel_8_1, 3, 3, "sel_8_1");
800 selaAddSel(sela, sel, NULL, 0);
801 sel = selCreateFromString(sel_8_2, 3, 3, "sel_8_2");
802 selaAddSel(sela, sel, NULL, 0);
803 sel = selCreateFromString(sel_8_3, 3, 3, "sel_8_3");
804 selaAddSel(sela, sel, NULL, 0);
805 sel = selCreateFromString(sel_8_4, 3, 3, "sel_8_4");
806 selaAddSel(sela, sel, NULL, 0);
807 sel = selCreateFromString(sel_8_5, 3, 3, "sel_8_5");
808 selaAddSel(sela, sel, NULL, 0);
809 sel = selCreateFromString(sel_8_6, 3, 3, "sel_8_6");
810 selaAddSel(sela, sel, NULL, 0);
811 sel = selCreateFromString(sel_8_7, 3, 3, "sel_8_7");
812 selaAddSel(sela, sel, NULL, 0);
813 sel = selCreateFromString(sel_8_8, 3, 3, "sel_8_8");
814 selaAddSel(sela, sel, NULL, 0);
815 sel = selCreateFromString(sel_8_9, 3, 3, "sel_8_9");
816 selaAddSel(sela, sel, NULL, 0);
817
818 return sela;
819 }
820
821
822 /*!
823 * \brief sela4and8ccThin()
824 *
825 * \param[in] sela [optional]
826 * \return sela with additional sels, or NULL on error
827 *
828 * <pre>
829 * Notes:
830 * (1) Adds the 2 basic sels for either 4-cc or 8-cc thinning.
831 * </pre>
832 */
833 SELA *
834 sela4and8ccThin(SELA *sela)
835 {
836 SEL *sel;
837
838 if (!sela) sela = selaCreate(2);
839
840 sel = selCreateFromString(sel_48_1, 3, 3, "sel_48_1");
841 selaAddSel(sela, sel, NULL, 0);
842 sel = selCreateFromString(sel_48_2, 3, 3, "sel_48_2");
843 selaAddSel(sela, sel, NULL, 0);
844
845 return sela;
846 }
847
848
849 /* -------------------------------------------------------------------------- *
850 * Other structuring elements *
851 * -------------------------------------------------------------------------- */
852 /*!
853 * \brief selMakePlusSign()
854 *
855 * \param[in] size side of containing square
856 * \param[in] linewidth of lines
857 * \return sel, or NULL on error
858 *
859 * <pre>
860 * Notes:
861 * (1) Useful for debugging to show location of selected pixels.
862 * (2) See displaySelectedPixels() for an example of use.
863 * </pre>
864 */
865 SEL *
866 selMakePlusSign(l_int32 size,
867 l_int32 linewidth)
868 {
869 PIX *pix;
870 SEL *sel;
871
872 if (size < 3 || linewidth > size)
873 return (SEL *)ERROR_PTR("invalid input", __func__, NULL);
874
875 pix = pixCreate(size, size, 1);
876 pixRenderLine(pix, size / 2, 0, size / 2, size - 1,
877 linewidth, L_SET_PIXELS);
878 pixRenderLine(pix, 0, size / 2, size, size / 2,
879 linewidth, L_SET_PIXELS);
880 sel = selCreateFromPix(pix, size / 2, size / 2, "plus_sign");
881 pixDestroy(&pix);
882 return sel;
883 }