1 /*====================================================================*
2 - Copyright (C) 2001 Leptonica. All rights reserved.
3 - This software is distributed in the hope that it will be
4 - useful, but with NO WARRANTY OF ANY KIND.
5 - No author or distributor accepts responsibility to anyone for the
6 - consequences of using this software, or for whether it serves any
7 - particular purpose or works at all, unless he or she says so in
8 - writing. Everyone is granted permission to copy, modify and
9 - redistribute this source code, for commercial or non-commercial
10 - purposes, with the following restrictions: (1) the origin of this
11 - source code must not be misrepresented; (2) modified versions must
12 - be plainly marked as such; and (3) this notice may not be removed
13 - or altered from any source or modified source distribution.
14 *====================================================================*/
15
16
17 /*
18 * sel2.c
19 *
20 * Contains definitions of simple structuring elements
21 *
22 * SELA *selaAddBasic()
23 * Linear horizontal and vertical
24 * Square
25 * Diagonals
26 *
27 * SELA *selaAddHitMiss()
28 * Isolated foreground pixel
29 * Horizontal and vertical edges
30 * Slanted edge
31 *
32 * SELA *selaAddDwaLinear()
33 * SELA *selaAddDwaCombs()
34 * SELA *selaAddCrossJunctions()
35 * SELA *selaAddTJunctions()
36 */
37
38 #include <stdio.h>
39 #include <math.h>
40 #include "allheaders.h"
41
42 /* MSVC can't handle arrays dimensioned by static const integers */
43 #define L_BUF_SIZE 512
44
45 /* Linear brick sel sizes, including all those that are required
46 * for decomposable sels up to size 63. */
47 static const l_int32 num_linear = 25;
48 static const l_int32 basic_linear[] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 20, 21, 25, 30, 31, 35, 40, 41, 45, 50, 51};
49
50
51 /*!
52 * selaAddBasic()
53 *
54 * Input: sela (<optional>)
55 * Return: sela with additional sels, or null on error
56 *
57 * Notes:
58 * (1) Adds the following sels:
59 * - all linear (horiz, vert) brick sels that are
60 * necessary for decomposable sels up to size 63
61 * - square brick sels up to size 10
62 * - 4 diagonal sels
63 */
64 SELA *
selaAddBasic(SELA * sela)65 selaAddBasic(SELA *sela)
66 {
67 char name[L_BUF_SIZE];
68 l_int32 i, size;
69 SEL *sel;
70
71 PROCNAME("selaAddBasic");
72
73 if (!sela) {
74 if ((sela = selaCreate(0)) == NULL)
75 return (SELA *)ERROR_PTR("sela not made", procName, NULL);
76 }
77
78 /*--------------------------------------------------------------*
79 * Linear horizontal and vertical sels *
80 *--------------------------------------------------------------*/
81 for (i = 0; i < num_linear; i++) {
82 size = basic_linear[i];
83 sel = selCreateBrick(1, size, 0, size / 2, 1);
84 snprintf(name, L_BUF_SIZE, "sel_%dh", size);
85 selaAddSel(sela, sel, name, 0);
86 }
87 for (i = 0; i < num_linear; i++) {
88 size = basic_linear[i];
89 sel = selCreateBrick(size, 1, size / 2, 0, 1);
90 snprintf(name, L_BUF_SIZE, "sel_%dv", size);
91 selaAddSel(sela, sel, name, 0);
92 }
93
94 /*-----------------------------------------------------------*
95 * 2-d Bricks *
96 *-----------------------------------------------------------*/
97 for (i = 2; i <= 5; i++) {
98 sel = selCreateBrick(i, i, i / 2, i / 2, 1);
99 snprintf(name, L_BUF_SIZE, "sel_%d", i);
100 selaAddSel(sela, sel, name, 0);
101 }
102
103 /*-----------------------------------------------------------*
104 * Diagonals *
105 *-----------------------------------------------------------*/
106 /* 0c 1
107 1 0 */
108 sel = selCreateBrick(2, 2, 0, 0, 1);
109 selSetElement(sel, 0, 0, 0);
110 selSetElement(sel, 1, 1, 0);
111 selaAddSel(sela, sel, "sel_2dp", 0);
112
113 /* 1c 0
114 0 1 */
115 sel = selCreateBrick(2, 2, 0, 0, 1);
116 selSetElement(sel, 0, 1, 0);
117 selSetElement(sel, 1, 0, 0);
118 selaAddSel(sela, sel, "sel_2dm", 0);
119
120 /* Diagonal, slope +, size 5 */
121 sel = selCreate(5, 5, "sel_5dp");
122 sel->cy = 2;
123 sel->cx = 2;
124 selSetElement(sel, 0, 4, 1);
125 selSetElement(sel, 1, 3, 1);
126 selSetElement(sel, 2, 2, 1);
127 selSetElement(sel, 3, 1, 1);
128 selSetElement(sel, 4, 0, 1);
129 selaAddSel(sela, sel, "sel_5dp", 0);
130
131 /* Diagonal, slope -, size 5 */
132 sel = selCreate(5, 5, "sel_5dm");
133 sel->cy = 2;
134 sel->cx = 2;
135 selSetElement(sel, 0, 0, 1);
136 selSetElement(sel, 1, 1, 1);
137 selSetElement(sel, 2, 2, 1);
138 selSetElement(sel, 3, 3, 1);
139 selSetElement(sel, 4, 4, 1);
140 selaAddSel(sela, sel, "sel_5dm", 0);
141
142 return sela;
143 }
144
145
146 /*!
147 * selaAddHitMiss()
148 *
149 * Input: sela (<optional>)
150 * Return: sela with additional sels, or null on error
151 */
152 SELA *
selaAddHitMiss(SELA * sela)153 selaAddHitMiss(SELA *sela)
154 {
155 SEL *sel;
156
157 PROCNAME("selaAddHitMiss");
158
159 if (!sela) {
160 if ((sela = selaCreate(0)) == NULL)
161 return (SELA *)ERROR_PTR("sela not made", procName, NULL);
162 }
163
164 #if 0 /* use just for testing */
165 sel = selCreateBrick(3, 3, 1, 1, 2);
166 selaAddSel(sela, sel, "sel_bad", 0);
167 #endif
168
169
170 /*--------------------------------------------------------------*
171 * Isolated foreground pixel *
172 *--------------------------------------------------------------*/
173 sel = selCreateBrick(3, 3, 1, 1, 2);
174 selSetElement(sel, 1, 1, 1);
175 selaAddSel(sela, sel, "sel_3hm", 0);
176
177
178 /*--------------------------------------------------------------*
179 * Horizontal and vertical edges *
180 *--------------------------------------------------------------*/
181 sel = selCreateBrick(2, 3, 0, 1, 1);
182 selSetElement(sel, 1, 0, 2);
183 selSetElement(sel, 1, 1, 2);
184 selSetElement(sel, 1, 2, 2);
185 selaAddSel(sela, sel, "sel_3de", 0);
186
187 sel = selCreateBrick(2, 3, 1, 1, 1);
188 selSetElement(sel, 0, 0, 2);
189 selSetElement(sel, 0, 1, 2);
190 selSetElement(sel, 0, 2, 2);
191 selaAddSel(sela, sel, "sel_3ue", 0);
192
193 sel = selCreateBrick(3, 2, 1, 0, 1);
194 selSetElement(sel, 0, 1, 2);
195 selSetElement(sel, 1, 1, 2);
196 selSetElement(sel, 2, 1, 2);
197 selaAddSel(sela, sel, "sel_3re", 0);
198
199 sel = selCreateBrick(3, 2, 1, 1, 1);
200 selSetElement(sel, 0, 0, 2);
201 selSetElement(sel, 1, 0, 2);
202 selSetElement(sel, 2, 0, 2);
203 selaAddSel(sela, sel, "sel_3le", 0);
204
205
206 /*--------------------------------------------------------------*
207 * Slanted edge *
208 *--------------------------------------------------------------*/
209 sel = selCreateBrick(13, 6, 6, 2, 0);
210 selSetElement(sel, 0, 3, 2);
211 selSetElement(sel, 0, 5, 1);
212 selSetElement(sel, 4, 2, 2);
213 selSetElement(sel, 4, 4, 1);
214 selSetElement(sel, 8, 1, 2);
215 selSetElement(sel, 8, 3, 1);
216 selSetElement(sel, 12, 0, 2);
217 selSetElement(sel, 12, 2, 1);
218 selaAddSel(sela, sel, "sel_sl1", 0);
219
220 return sela;
221 }
222
223
224 /*!
225 * selaAddDwaLinear()
226 *
227 * Input: sela (<optional>)
228 * Return: sela with additional sels, or null on error
229 *
230 * Notes:
231 * (1) Adds all linear (horizontal, vertical) sels from
232 * 2 to 63 pixels in length, which are the sizes over
233 * which dwa code can be generated.
234 */
235 SELA *
selaAddDwaLinear(SELA * sela)236 selaAddDwaLinear(SELA *sela)
237 {
238 char name[L_BUF_SIZE];
239 l_int32 i;
240 SEL *sel;
241
242 PROCNAME("selaAddDwaLinear");
243
244 if (!sela) {
245 if ((sela = selaCreate(0)) == NULL)
246 return (SELA *)ERROR_PTR("sela not made", procName, NULL);
247 }
248
249 for (i = 2; i < 64; i++) {
250 sel = selCreateBrick(1, i, 0, i / 2, 1);
251 snprintf(name, L_BUF_SIZE, "sel_%dh", i);
252 selaAddSel(sela, sel, name, 0);
253 }
254 for (i = 2; i < 64; i++) {
255 sel = selCreateBrick(i, 1, i / 2, 0, 1);
256 snprintf(name, L_BUF_SIZE, "sel_%dv", i);
257 selaAddSel(sela, sel, name, 0);
258 }
259 return sela;
260 }
261
262
263 /*!
264 * selaAddDwaCombs()
265 *
266 * Input: sela (<optional>)
267 * Return: sela with additional sels, or null on error
268 *
269 * Notes:
270 * (1) Adds all comb (horizontal, vertical) Sels that are
271 * used in composite linear morphological operations
272 * up to 63 pixels in length, which are the sizes over
273 * which dwa code can be generated.
274 */
275 SELA *
selaAddDwaCombs(SELA * sela)276 selaAddDwaCombs(SELA *sela)
277 {
278 char name[L_BUF_SIZE];
279 l_int32 i, f1, f2, prevsize, size;
280 SEL *selh, *selv;
281
282 PROCNAME("selaAddDwaCombs");
283
284 if (!sela) {
285 if ((sela = selaCreate(0)) == NULL)
286 return (SELA *)ERROR_PTR("sela not made", procName, NULL);
287 }
288
289 prevsize = 0;
290 for (i = 4; i < 64; i++) {
291 selectComposableSizes(i, &f1, &f2);
292 size = f1 * f2;
293 if (size == prevsize)
294 continue;
295 selectComposableSels(i, L_HORIZ, NULL, &selh);
296 selectComposableSels(i, L_VERT, NULL, &selv);
297 snprintf(name, L_BUF_SIZE, "sel_comb_%dh", size);
298 selaAddSel(sela, selh, name, 0);
299 snprintf(name, L_BUF_SIZE, "sel_comb_%dv", size);
300 selaAddSel(sela, selv, name, 0);
301 prevsize = size;
302 }
303
304 return sela;
305 }
306
307
308 /*!
309 * selaAddCrossJunctions()
310 *
311 * Input: sela (<optional>)
312 * hlsize (length of each line of hits from origin)
313 * mdist (distance of misses from the origin)
314 * norient (number of orientations; max of 8)
315 * debugflag (1 for debug output)
316 * Return: sela with additional sels, or null on error
317 *
318 * Notes:
319 * (1) Adds hitmiss Sels for the intersection of two lines.
320 * If the lines are very thin, they must be nearly orthogonal
321 * to register.
322 * (2) The number of Sels generated is equal to @norient.
323 * (3) If @norient == 2, this generates 2 Sels of crosses, each with
324 * two perpendicular lines of hits. One Sel has horizontal and
325 * vertical hits; the other has hits along lines at +-45 degrees.
326 * Likewise, if @norient == 3, this generates 3 Sels of crosses
327 * oriented at 30 degrees with each other.
328 * (4) It is suggested that @hlsize be chosen at least 1 greater
329 * than @mdist. Try values of (@hlsize, @mdist) such as
330 * (6,5), (7,6), (8,7), (9,7), etc.
331 */
332 SELA *
selaAddCrossJunctions(SELA * sela,l_float32 hlsize,l_float32 mdist,l_int32 norient,l_int32 debugflag)333 selaAddCrossJunctions(SELA *sela,
334 l_float32 hlsize,
335 l_float32 mdist,
336 l_int32 norient,
337 l_int32 debugflag)
338 {
339 char name[L_BUF_SIZE];
340 l_int32 i, j, w, xc, yc;
341 l_float64 pi, halfpi, radincr, radang;
342 l_float64 angle;
343 PIX *pixc, *pixm, *pixt;
344 PIXA *pixa;
345 PTA *pta1, *pta2, *pta3, *pta4;
346 SEL *sel;
347
348 PROCNAME("selaAddCrossJunctions");
349
350 if (hlsize <= 0)
351 return (SELA *)ERROR_PTR("hlsize not > 0", procName, NULL);
352 if (norient < 1 || norient > 8)
353 return (SELA *)ERROR_PTR("norient not in [1, ... 8]", procName, NULL);
354
355 if (!sela) {
356 if ((sela = selaCreate(0)) == NULL)
357 return (SELA *)ERROR_PTR("sela not made", procName, NULL);
358 }
359
360 pi = 3.1415926535;
361 halfpi = 3.1415926535 / 2.0;
362 radincr = halfpi / (l_float64)norient;
363 w = (l_int32)(2.2 * (L_MAX(hlsize, mdist) + 0.5));
364 if (w % 2 == 0)
365 w++;
366 xc = w / 2;
367 yc = w / 2;
368
369 pixa = pixaCreate(norient);
370 for (i = 0; i < norient; i++) {
371
372 /* Set the don't cares */
373 pixc = pixCreate(w, w, 32);
374 pixSetAll(pixc);
375
376 /* Add the green lines of hits */
377 pixm = pixCreate(w, w, 1);
378 radang = (l_float32)i * radincr;
379 pta1 = generatePtaLineFromPt(xc, yc, hlsize + 1, radang);
380 pta2 = generatePtaLineFromPt(xc, yc, hlsize + 1, radang + halfpi);
381 pta3 = generatePtaLineFromPt(xc, yc, hlsize + 1, radang + pi);
382 pta4 = generatePtaLineFromPt(xc, yc, hlsize + 1, radang + pi + halfpi);
383 ptaJoin(pta1, pta2, 0, 0);
384 ptaJoin(pta1, pta3, 0, 0);
385 ptaJoin(pta1, pta4, 0, 0);
386 pixRenderPta(pixm, pta1, L_SET_PIXELS);
387 pixPaintThroughMask(pixc, pixm, 0, 0, 0x00ff0000);
388 ptaDestroy(&pta1);
389 ptaDestroy(&pta2);
390 ptaDestroy(&pta3);
391 ptaDestroy(&pta4);
392
393 /* Add red misses between the lines */
394 for (j = 0; j < 4; j++) {
395 angle = radang + (j - 0.5) * halfpi;
396 pixSetPixel(pixc, xc + (l_int32)(mdist * cos(angle)),
397 yc + (l_int32)(mdist * sin(angle)), 0xff000000);
398 }
399
400 /* Add dark green for origin */
401 pixSetPixel(pixc, xc, yc, 0x00550000);
402
403 /* Generate the sel */
404 sel = selCreateFromColorPix(pixc, NULL);
405 sprintf(name, "sel_cross_%d", i);
406 selaAddSel(sela, sel, name, 0);
407
408 if (debugflag) {
409 pixt = pixScaleBySampling(pixc, 10.0, 10.0);
410 pixaAddPix(pixa, pixt, L_INSERT);
411 }
412 pixDestroy(&pixm);
413 pixDestroy(&pixc);
414 }
415
416 if (debugflag) {
417 l_int32 w;
418 pixaGetPixDimensions(pixa, 0, &w, NULL, NULL);
419 pixt = pixaDisplayTiledAndScaled(pixa, 32, w, 1, 0, 10, 2);
420 pixWrite("/tmp/junkxsel1.png", pixt, IFF_PNG);
421 pixDisplay(pixt, 0, 100);
422 pixDestroy(&pixt);
423 pixt = selaDisplayInPix(sela, 15, 2, 20, 1);
424 pixWrite("/tmp/junkxsel2.png", pixt, IFF_PNG);
425 pixDisplay(pixt, 500, 100);
426 pixDestroy(&pixt);
427 selaWriteStream(stderr, sela);
428 }
429 pixaDestroy(&pixa);
430
431 return sela;
432 }
433
434
435 /*!
436 * selaAddTJunctions()
437 *
438 * Input: sela (<optional>)
439 * hlsize (length of each line of hits from origin)
440 * mdist (distance of misses from the origin)
441 * norient (number of orientations; max of 8)
442 * debugflag (1 for debug output)
443 * Return: sela with additional sels, or null on error
444 *
445 * Notes:
446 * (1) Adds hitmiss Sels for the T-junction of two lines.
447 * If the lines are very thin, they must be nearly orthogonal
448 * to register.
449 * (2) The number of Sels generated is 4 * @norient.
450 * (3) It is suggested that @hlsize be chosen at least 1 greater
451 * than @mdist. Try values of (@hlsize, @mdist) such as
452 * (6,5), (7,6), (8,7), (9,7), etc.
453 */
454 SELA *
selaAddTJunctions(SELA * sela,l_float32 hlsize,l_float32 mdist,l_int32 norient,l_int32 debugflag)455 selaAddTJunctions(SELA *sela,
456 l_float32 hlsize,
457 l_float32 mdist,
458 l_int32 norient,
459 l_int32 debugflag)
460 {
461 char name[L_BUF_SIZE];
462 l_int32 i, j, k, w, xc, yc;
463 l_float64 pi, halfpi, radincr, jang, radang;
464 l_float64 angle[3], dist[3];
465 PIX *pixc, *pixm, *pixt;
466 PIXA *pixa;
467 PTA *pta1, *pta2, *pta3;
468 SEL *sel;
469
470 PROCNAME("selaAddTJunctions");
471
472 if (hlsize <= 2)
473 return (SELA *)ERROR_PTR("hlsizel not > 1", procName, NULL);
474 if (norient < 1 || norient > 8)
475 return (SELA *)ERROR_PTR("norient not in [1, ... 8]", procName, NULL);
476
477 if (!sela) {
478 if ((sela = selaCreate(0)) == NULL)
479 return (SELA *)ERROR_PTR("sela not made", procName, NULL);
480 }
481
482 pi = 3.1415926535;
483 halfpi = 3.1415926535 / 2.0;
484 radincr = halfpi / (l_float32)norient;
485 w = (l_int32)(2.4 * (L_MAX(hlsize, mdist) + 0.5));
486 if (w % 2 == 0)
487 w++;
488 xc = w / 2;
489 yc = w / 2;
490
491 pixa = pixaCreate(4 * norient);
492 for (i = 0; i < norient; i++) {
493 for (j = 0; j < 4; j++) { /* 4 orthogonal orientations */
494 jang = (l_float32)j * halfpi;
495
496 /* Set the don't cares */
497 pixc = pixCreate(w, w, 32);
498 pixSetAll(pixc);
499
500 /* Add the green lines of hits */
501 pixm = pixCreate(w, w, 1);
502 radang = (l_float32)i * radincr;
503 pta1 = generatePtaLineFromPt(xc, yc, hlsize + 1, jang + radang);
504 pta2 = generatePtaLineFromPt(xc, yc, hlsize + 1,
505 jang + radang + halfpi);
506 pta3 = generatePtaLineFromPt(xc, yc, hlsize + 1,
507 jang + radang + pi);
508 ptaJoin(pta1, pta2, 0, 0);
509 ptaJoin(pta1, pta3, 0, 0);
510 pixRenderPta(pixm, pta1, L_SET_PIXELS);
511 pixPaintThroughMask(pixc, pixm, 0, 0, 0x00ff0000);
512 ptaDestroy(&pta1);
513 ptaDestroy(&pta2);
514 ptaDestroy(&pta3);
515
516 /* Add red misses between the lines */
517 angle[0] = radang + jang - halfpi;
518 angle[1] = radang + jang + 0.5 * halfpi;
519 angle[2] = radang + jang + 1.5 * halfpi;
520 dist[0] = 0.8 * mdist;
521 dist[1] = dist[2] = mdist;
522 for (k = 0; k < 3; k++) {
523 pixSetPixel(pixc, xc + (l_int32)(dist[k] * cos(angle[k])),
524 yc + (l_int32)(dist[k] * sin(angle[k])),
525 0xff000000);
526 }
527
528 /* Add dark green for origin */
529 pixSetPixel(pixc, xc, yc, 0x00550000);
530
531 /* Generate the sel */
532 sel = selCreateFromColorPix(pixc, NULL);
533 sprintf(name, "sel_cross_%d", 4 * i + j);
534 selaAddSel(sela, sel, name, 0);
535
536 if (debugflag) {
537 pixt = pixScaleBySampling(pixc, 10.0, 10.0);
538 pixaAddPix(pixa, pixt, L_INSERT);
539 }
540 pixDestroy(&pixm);
541 pixDestroy(&pixc);
542 }
543 }
544
545 if (debugflag) {
546 l_int32 w;
547 pixaGetPixDimensions(pixa, 0, &w, NULL, NULL);
548 pixt = pixaDisplayTiledAndScaled(pixa, 32, w, 4, 0, 10, 2);
549 pixWrite("/tmp/junktsel1.png", pixt, IFF_PNG);
550 pixDisplay(pixt, 0, 100);
551 pixDestroy(&pixt);
552 pixt = selaDisplayInPix(sela, 15, 2, 20, 4);
553 pixWrite("/tmp/junktsel2.png", pixt, IFF_PNG);
554 pixDisplay(pixt, 500, 100);
555 pixDestroy(&pixt);
556 selaWriteStream(stderr, sela);
557 }
558 pixaDestroy(&pixa);
559
560 return sela;
561 }
562
563