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