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